gjoi 2024.9.12

Hypoxia571發表於2024-09-13

T1 星天花雨

首先考慮分解 \(k=l\times r\),然後考慮 \(a/b\) 的字首和中差分為 \(l/r\) 的對數是多少累加進去就行了,這個是好求的。

#include<bits/stdc++.h>
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back

using namespace std;

const int N=100005, P=998244353;

int n, m, k, a[N], b[N], L[N], R[N], Ans, flag=1;

void solve(int l,int r) {
	if(l>a[n]||r>b[m]) return;
	if(flag) {
		(Ans+=1ll*(n-l+1)*(m-r+1)%P)%=P;
		return;
	}
	int cnt1=0, cnt2=0;
	up(i,0,a[n]-l) (cnt1+=1ll*L[i]*L[i+l]%P)%=P;
	up(i,0,b[m]-r) (cnt2+=1ll*R[i]*R[i+r]%P)%=P;
	(Ans+=1ll*cnt1*cnt2%P)%=P;
}

signed main() {
	freopen("rain.in","r",stdin);
	freopen("rain.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m >> k, L[0]=R[0]=1;
	up(i,1,n) cin >> a[i], a[i]+=a[i-1], ++L[a[i]];
	up(i,1,m) cin >> b[i], b[i]+=b[i-1], ++R[b[i]];
	up(i,1,n) if(a[i]!=a[i-1]+1) flag=0;
	up(i,1,m) if(b[i]!=b[i-1]+1) flag=0;
	for(int i=1; i*i<=k; ++i) if(k%i==0) {
		solve(i,k/i);
		if(i*i!=k) solve(k/i,i);
	}
	cout << Ans;
	return 0;
}

T2 野火

Bronya 可愛,多測全塞二元環不可愛(二元環二分上界要給到 \(3\times 10^9\)) /fn

就是先考慮二分,樹的情況是簡單的,二分之後不夠的補上得了。

首先基環樹中那一部分無用,只考慮環,我們先補滿,然後時刻裡面環上可以斷一條邊,我們考慮怎麼斷最優,首先會想去拿那個 \(d_i\) 最小的斷掉,拿回那 \(mid-d_i\) 的部分,然後發現前面 \(d_i\) 時刻還可以斷別的,那就考慮給 \(d_i\) 次小的吃這個貢獻,所有可能的貢獻這個次小一定能吃滿,然後就做完了。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_back

using namespace std;

const int N=200005, inf=1ll<<60;

int n, m, sp, in[N], u[N], v[N], d[N], vis[N];
vector<int> to[N]; queue<int> q; 

void solve1() {
	int l=1, r=3e9, Ans;
	while(l<=r) {
		int mid=(l+r)>>1, ret=0;
		up(i,1,m) if(d[i]<mid) ret+=mid-d[i];
		if(ret<=sp) Ans=mid, l=mid+1;
		else r=mid-1;
	}
	cout << Ans << '\n';
}

void solve2() {
	up(i,1,n) vis[i]=0;
	up(i,1,n) if(in[i]==1) q.push(i);
	while(q.size()) {
		int x=q.front();
		vis[x]=1, q.pop();
		for(int y:to[x]) if(--in[y]==1) q.push(y);
	}
	int l=1, r=3e9, Ans;
	while(l<=r) {
		int mid=(l+r)>>1, ret=0, fir=inf, sec=inf;
		up(i,1,m) if((vis[u[i]]||vis[v[i]])&&d[i]<mid) ret+=mid-d[i];
		up(i,1,m) if(!vis[u[i]]&&!vis[v[i]]&&d[i]<mid) {
			ret+=mid-d[i];
			if(d[i]<fir) sec=fir, fir=d[i];
			else if(d[i]<sec) sec=d[i];
		}
		if(fir<inf) ret-=mid-fir;
		if(sec<inf) ret-=max(mid-sec,0ll)-max(mid-fir-sec,0ll);
		if(ret<=sp) Ans=mid, l=mid+1;
		else r=mid-1;
	}
	cout << Ans << '\n';
}

void mian() {
	cin >> n >> m >> sp;
	up(i,1,n) in[i]=0, to[i].clear();
	up(i,1,m) {
		cin >> u[i] >> v[i] >> d[i];
		++in[u[i]], ++in[v[i]];
		to[u[i]].pb(v[i]), to[v[i]].pb(u[i]);
	} 
	if(m==n-1) solve1(); else solve2();
	
}

signed main() {
	freopen("wildfire.in","r",stdin);
	freopen("wildfire.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	int id, T;
	cin >> id >> T;
	while(T--) mian();
	return 0;
}

T3 赤玉琉金

image

點對能產生貢獻當且僅當一個點集 \(S\) 沒有被刪除(如上圖綠色部分),考慮 \(|S|=x\) 的點對的貢獻,不難寫出 \(Ans_x=\sum_{t=0}^n\binom{n-x}{t}\times t!\times (n-t)!=\frac{(n-x)!}{(n-x-t)!}\times (n-t)!\),常數 \((n-x)!\) 提出來得到 \(Ans_x=(n-x)!\times \sum_{t=0}^n\frac{(n-t)!}{(n-x-t)!}\),後面那部分還是不好求,但是補成組合數可以組合意義,\(Ans_x=(n-x)!x!\times \sum_{t=0}^n\binom{n-t}{x}=(n-x)!x!\times \sum_{t=0}^n\binom{t}{x}\)

相關文章