第1屆ICPC青少年程序設計競賽 部分題目題解


比賽鏈接

C.Chocolate Counting

給定質數 \(p\)\(p\ge 3\))與整數 \(k\),求在 \(1\sim kp\) 中選擇 \(p\) 個互不相同的數,滿足和為 \(p\) 的倍數的方案數。\(p,k\le 10^7\)

Solution

對於某些模質數 \(p\) 的余數相關的計數題,可以考慮將選擇方案分組,滿足每組中 \(p\) 個方案的結果模 \(p\) 余數方案互不相同。

\(1\sim kp\) 放在一個 \(p\times k\) 的矩陣上,第 \(i\) 行依次放置 \(i,i+p,i+2p,\dots i+(k-1)p\)

考慮在矩陣上隨機選擇 \(p\) 個數,如果這 \(p\) 個數恰好是同一列的 \(p\) 個數,那么它們直接合法。否則,考慮找到從左到右任意一個被選了數的列,將其上被選擇的數循環移位,得到 \(p\) 個選擇方案,那么顯然有這 \(p\) 個選擇方案 \(\mod p\) 的余數恰好為 \(0\sim p-1\)

由此,可以將剩下的所有 \(\dbinom{pk}{p}-k\) 中方案,\(p\) 個為一組分成若干組,每組中恰好有一個方案合法,因此這部分對答案的貢獻就是 \(\dfrac{1}{p}(\dbinom{pk}{p}-k)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353,N=1e7+10;
int T,p,k,fac[N],inv[N];
inline int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
inline int dec(int x,int y){return (x-y<0)?x-y+mod:x-y;}
inline void init(int n){
	inv[0]=inv[1]=1;
	for(int i=2;i<=n;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
int main(){
	scanf("%d",&T);
	init(10000000);
	while(T--){
		scanf("%d%d",&p,&k);
		ll now=1ll*p*k;int ans=1;
		for(int i=1;i<=p;++i) ans=1ll*ans*(now%mod)%mod*inv[i]%mod,now--;
		printf("%d\n",add(1ll*dec(ans,k)*inv[p]%mod,k));
	}
	return 0;
}

G.Dynamic Graph

給定一個 \(n\) 個點的無向圖以及一個常數 \(F\),一開始沒有邊。

你需要執行 \(m\) 次操作。

操作有 \(3\) 種:

1 u v w: 在 \(u\)\(v\) 之間加了一條權值為 \(w\) 的邊。

2 id: 刪除第 \(id\) 次操作添加的邊。

3 u v w: 詢問是否存在一條權值和模 \(F\)\(w\) 的從 \(u\)\(v\) 的路徑(可以不是簡單路徑)。

\(n,m\le 10^5,F\le 10^9\)

Solution

經典套路:對於詢問某個兩個點之間是否存在路徑權值 \(\mod F=w\) 的問題。可以先取出兩點之間任意一條路徑,設長度為 \(x\),求出兩點所在連通塊所有環長的 \(\gcd\) 設為\(G\),那么能取到 \(w\) 當且僅當 \(w\equiv kG+x\pmod F(k\in Z)\)

對這個結論的證明也很簡單,就是先取出任意一條路徑,然后可以通過從某個點出發向環上任意一點來回走 \(F\) 遍,在中途繞環若干圈將環長疊加進路徑答案中。

那么對於原問題,我們首先通過線段樹分治去掉刪除操作。對於添加邊 \((u,v,w)\) 的操作,如果 \(u,v\) 在同一連通塊中,設 \(d_u,d_v\) 分別為 \(u,v\) 到根的路徑長,則更新該連通塊最大公約數 \(G=\gcd(G,2w,w+d_u+d_v)\)。如果 \(u,v\) 不在同一連通塊中,合並這兩個連通塊,新塊的最大公約數 \(G=\gcd(G_1,G_2,2w)\)

於是使用按秩合並並查集維護,復雜度為 \(\mathcal O(n\log ^2 n)\)(假設 \(n,m\) 同階)。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct edge{
	int u,v,w;
}e[N];
int n,m,tot,id[N],mod,st[N],ed[N];
inline int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
inline int dec(int x,int y){return (x-y<0)?x-y+mod:x-y;}
vector<edge> que[N];

namespace DSU{
	int fa[N],siz[N],up[N],g[N],top;
	int stk[N][6];
	inline int find(int x){return fa[x]==x?fa[x]:find(fa[x]);}
	inline int gval(int x){return fa[x]==x?0:add(gval(fa[x]),up[x]);}
	inline void merge(int u,int v,int w){
		int x=add(gval(u),gval(v));
		u=find(u);v=find(v);
		if(siz[u]<siz[v]) swap(u,v);
		++top;
		stk[top][0]=u;stk[top][1]=v;stk[top][2]=g[u];
		stk[top][3]=fa[v];stk[top][4]=siz[u];stk[top][5]=up[v];
		if(u==v){
			g[u]=__gcd(g[u],__gcd(add(w,w),add(w,x)));
			return ;
		}
		fa[v]=u;siz[u]+=siz[v];up[v]=add(x,w);
		g[u]=__gcd(g[u],__gcd(g[v],add(w,w))); 
	}
	inline void rollback(int x){
		while(top>x){
			int u=stk[top][0],v=stk[top][1];
			g[u]=stk[top][2];fa[v]=stk[top][3];
			siz[u]=stk[top][4];up[v]=stk[top][5];
			--top;
		}
	}
	inline void qry(int x,int u,int v,int w){
		int now=dec(w,add(gval(u),gval(v)));
		if(now%g[x]==0) puts("1");
		else puts("0");
	}
}
using namespace DSU;
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid ((l+r)>>1)
	vector<int>tr[N<<2];
	inline void update(int p,int ql,int qr,int x,int l=1,int r=m){
		if(ql<=l&&r<=qr){tr[p].push_back(x);return ;}
		if(ql<=mid) update(lc,ql,qr,x,l,mid);
		if(qr>mid) update(rc,ql,qr,x,mid+1,r); 
	}
	inline void solve(int p=1,int l=1,int r=m){
		int now=top;
		for(int x:tr[p]) merge(e[x].u,e[x].v,e[x].w);
		if(l==r){
			for(auto e:que[l]){
				if(find(e.u)!=find(e.v)) puts("0");
				else qry(find(e.u),e.u,e.v,e.w);
			}
		}
		if(l!=r) solve(lc,l,mid),solve(rc,mid+1,r);
		rollback(now);
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&mod);
	for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1,g[i]=mod;
	for(int i=1;i<=m;++i){
		int op;scanf("%d",&op);
		if(op==1){
			++tot;id[i]=tot;
			scanf("%d%d%d",&e[tot].u,&e[tot].v,&e[tot].w);
			st[tot]=i;ed[tot]=-1;
		}
		else if(op==2){
			int x;
			scanf("%d",&x);
			ed[id[x]]=i;
		}
		else{
			int u,v,w;scanf("%d%d%d",&u,&v,&w);
			que[i].push_back((edge){u,v,w});
		}
	}
	for(int i=1;i<=tot;++i){
		if(~ed[i]) SGT::update(1,st[i],ed[i],i);
		else SGT::update(1,st[i],m,i);
	}
	SGT::solve();
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM