LOJ3235 Przedszkole 和 有限空间跳跃理论

Przedszkole

有一个 \(n\) 个点 \(m\) 条边的无向图,每个点从 \(1\)\(n\) 编号。你有 \(k\) 种颜色,要给每个点染色,使得有边相连的两个点颜色不一样。求出染色方案数,对 \(10^9+7\) 取模。

对于 \(100\%\) 的数据,\(1 \le n \le 10^5, 0 \le m \le \min(10^5, n(n-1)/2), 1 \le z \le 1000, 1 \le a_i, b_i \le n, 1 \le k_i \le 10^9\)

Subtask # 额外限制 分值
1 \(n \le 8, k \le 8, z \le 50\) \(8\)
2 \(n \le 15\) \(26\)
3 \(m \le 24\) \(33\)
4 每个点恰好有两条边和它相连 \(33\)

题解

http://jklover.hs-blog.cf/2020/06/09/Loj-3235-Przedszkole/#more

容斥原理 + 子集卷积 + 拉格朗日插值 + 色多项式.

对三种子任务分别设计算法.

\(m\le 24\)

考虑容斥,暴力枚举哪些边连接的两个点颜色是相同的,用并查集维护相同颜色的点形成的连通块.

若钦定了 \(p​\) 条边连接的两个点颜色相同,形成了 \(s​\) 个连通块,贡献应为 \((-1)^p k^s​\) .

由于每加一条边最多减少 \(1​\) 个连通块,所以 \(s​\)\(n​\) 的差不会超过 \(m​\) ,维护出这个关于 \(k​\) 的多项式各项系数即可.

时间复杂度 \(O(2^mm+qm)​\) .

\(n\le 15\)

\(m\le 24\) 的做法可以注意到答案是关于 \(k\)\(n\) 次多项式.

于是只需要代 \(k=1,2,3\dots,n+1\) 求出答案,就可以插值得出这个多项式.

考虑对于一个 \(k\) 如何求出答案.

染色可以看成依次为每种颜色确定点集,每种颜色的点集必须是独立集,不同颜色的点集不能相交.

记集合幂级数 \(F(x)=\sum_{S\in I} x^S\) ,其中 \(I\) 表示所有独立集的集合,则 \(F\) 子集卷积意义下 \(k\) 次幂全集的系数即为所求.

时间复杂度 \(O(2^nn^3+qn)\) .

每个点度数为 \(2\)

此时的图一定是由若干个互不相交的环构成的,考虑一个长度为 \(l\) 的环,它的色多项式为 \((k-1)^l+(-1)^l(k-1)\) .

考虑容斥。首先有\(k(k-1)^{l-1}\),然后这样会多算进去\(1\)\(l\)相同的。首尾相同把它们合并起来,看成长度为\(l-1\)的环的方案。最后到环长为\(2\)的时候停止。

\[\sum_{i=2}^l(-1)^{l-i}k(k-1)^{i-1}\\ =\sum_{i=2}^l(-1)^{l-i}((k-1)^i+(k-1)^{i-1})\\ =(k-1)^l+(-1)^{l-2}(k-1) \]

注意长度相同的环是没有本质差别的,可以合并起来一起算.

环长种类数是 \(O(\sqrt n)\) 的,时间复杂度 \(O(q\sqrt n\log n)\) .

namespace Task1{
	CO int N=1e5+10,M=25;
	int fa[N],siz[N];
	int st[M],ed[M],coef[M];
	
	int find(int x){
		return fa[x]==x?x:find(fa[x]);
	}
	void main(int n,int m,int q){
		iota(fa+1,fa+n+1,1);
		fill(siz+1,siz+n+1,1);
		for(int i=0;i<m;++i) read(st[i]),read(ed[i]);
		function<void(int,int,int)> dfs=[&](int x,int p,int s)->void{
			if(x==m){
				coef[s]=add(coef[s],p);
				return;
			}
			int u=find(st[x]),v=find(ed[x]);
			if(u!=v){
				dfs(x+1,p,s);
				if(siz[u]>siz[v]) swap(u,v);
				fa[u]=v,siz[v]+=siz[u];
				dfs(x+1,mod-p,s+1);
				fa[u]=u,siz[v]-=siz[u];
			}
		};
		dfs(0,1,0);
		while(q--){
			int k=read<int>(),ans=0;
			int mi=max(1,n-m),pw=fpow(k,mi);
			for(int i=mi;i<=n;++i){
				ans=add(ans,mul(coef[n-i],pw));
				pw=mul(pw,k);
			}
			printf("%d\n",ans);
		}
	}
}

namespace Task2{
	CO int N=15;
	int nxt[N],popc[1<<N],y[N+1];
	int a[N+1][1<<N],res[N+1][1<<N],tmp[N+1][1<<N];
	
	void FWT(int a[],int n,int dir){
		for(int i=0;i<n;++i)
			for(int mask=0;mask<1<<n;++mask)if(mask>>i&1){
				int t=a[mask^1<<i];
				if(dir) t=mod-t;
				a[mask]=add(a[mask],t);
			}
	}
	void main(int n,int m,int q){
		for(int i=0;i<m;++i){
			int u=read<int>()-1,v=read<int>()-1;
			nxt[u]|=1<<v,nxt[v]|=1<<u;
		}
		for(int mask=0;mask<1<<n;++mask){
			popc[mask]=popc[mask>>1]+(mask&1);
			bool f=1;
			for(int i=0;i<n and f;++i)if(mask>>i&1)
				if(mask&nxt[i]) f=0;
			if(f) a[popc[mask]][mask]=1;
		}
		res[0][0]=1;
		for(int i=0;i<=n;++i) FWT(a[i],n,0),FWT(res[i],n,0);
		y[0]=0;
		for(int k=1;k<=n;++k){
			for(int i=0;i<=n;++i) fill(tmp[i],tmp[i]+(1<<n),0);
			for(int i=0;i<=n;++i)
				for(int mask=0;mask<1<<n;++mask)if(a[i][mask])
					for(int j=0;i+j<=n;++j)
						tmp[i+j][mask]=add(tmp[i+j][mask],mul(a[i][mask],res[j][mask]));
			for(int i=0;i<=n;++i) copy(tmp[i],tmp[i]+(1<<n),res[i]);
			FWT(tmp[n],n,1);
			y[k]=tmp[n][(1<<n)-1];
		}
		while(q--){
			int k=read<int>();
			if(k<=n){
				printf("%d\n",y[k]);
				continue;
			}
			int ans=0;
			for(int i=0;i<=n;++i){
				int t=y[i];
				for(int j=0;j<=n;++j)if(j!=i){
					t=mul(t,k+mod-j);
					t=mul(t,fpow(i+mod-j,mod-2));
				}
				ans=add(ans,t);
			}
			printf("%d\n",ans);
		}
	}
}

namespace Task3{
	CO int N=1e5+10;
	int vis[N],siz[N];
	vector<int> to[N];
	int a[N],b[N];
	
	int dfs(int x){
		vis[x]=1;
		int s=1;
		for(int y:to[x])if(!vis[y]) s+=dfs(y);
		return s;
	}
	void main(int n,int m,int q){
		for(int i=1;i<=n;++i){
			int u=read<int>(),v=read<int>();
			to[u].push_back(v),to[v].push_back(u);
		}
		int tot=0;
		for(int i=1;i<=n;++i)if(!vis[i]) siz[++tot]=dfs(i);
		sort(siz+1,siz+tot+1);
		int c=0;
		for(int l=1,r=1;l<=tot;l=r){
			a[++c]=siz[l];
			for(;r<=n and siz[r]==siz[l];++r) ++b[c];
		}
		while(q--){
			int k=read<int>(),ans=1;
			for(int i=1;i<=c;++i){
				int t=fpow(k-1,a[i]);
				if(a[i]&1) t=add(t,mod-(k-1));
				else t=add(t,k-1);
				ans=mul(ans,fpow(t,b[i]));
			}
			printf("%d\n",ans);
		}
	}
}

int main(){
	int n=read<int>(),m=read<int>(),q=read<int>();
	if(m<=24) Task1::main(n,m,q);
	else if(n<=15) Task2::main(n,m,q);
	else Task3::main(n,m,q);
	return 0;
}

有限空间跳跃理论

给出一个无向连通图,求给每条边定向后是DAG(有向无环图)的方案数,两种方案不同当且仅当存在一条边它们的方向不同。

\(n\leq 20\)

题解

https://blog.csdn.net/sadnohappy/article/details/89389217

\(f(S)\)表示集合\(S\)的点在DAG上的方案数,转移时枚举一个独立集\(T\)表示度数为\(0\)的点,大概转移时这样

\[f(S)=\sum_{T\subset S}f(S-T)(-1)^{|T|-1} \]

这个系数是怎么来的呢?考虑每次都可以只加一个点,但是如果两个点独立的话,那么他们两个谁先谁后没有区别,所以算重了。归纳一下容斥系数应该\((-1)^{|T|-1}\)

直接枚举是\(3^n\)的,需要子集卷积优化。\(O(2^nn^2)\)

CO int N=20;
int a[N];
int f[N+1][1<<N],g[N+1][1<<N];

void FWT(int a[],int n,int dir){
	for(int i=0;i<n;++i)
		for(int mask=0;mask<1<<n;++mask)if(mask>>i&1){
			int t=a[mask^1<<i];
			if(dir) t=mod-t;
			a[mask]=add(a[mask],t);
		}
}
int main(){
	freopen("jump.in","r",stdin),freopen("jump.out","w",stdout);
	int n=read<int>();
	for(int m=read<int>();m--;){
		int u=read<int>()-1,v=read<int>()-1;
		a[u]|=1<<v,a[v]|=1<<u;
	}
	for(int mask=1;mask<1<<n;++mask){
		bool flag=1;
		for(int i=0;i<n and flag;++i)if(mask>>i&1)
			if(mask&a[i]) flag=0;
		if(!flag) continue;
		g[popcount(mask)][mask]=add(g[popcount(mask)][mask],popcount(mask)%2==1?1:mod-1);
	}
	for(int i=0;i<=n;++i) FWT(g[i],n,0);
	f[0][0]=1;
	for(int i=1;i<=n;++i){
		FWT(f[i-1],n,0);
		for(int j=0;j<=i-1;++j)for(int mask=0;mask<1<<n;++mask)
			f[i][mask]=add(f[i][mask],mul(f[j][mask],g[i-j][mask]));
		FWT(f[i],n,1);
		for(int mask=0;mask<1<<n;++mask)
			if(popcount(mask)!=i) f[i][mask]=0;
	}
	printf("%d\n",f[n][(1<<n)-1]);
	return 0;
}

色多项式

color_poly

其实\(k\)染色可以这样看:在子集卷积的过程中顺便带上颜色的方案数,这样因为枚举子集没有钦定编号最小的点必须选,所以多乘了一个\(k!\),最后除掉一个\(k!\)

那么\(k\)染色相当于代入了\(k\)的下降幂。\(-1\)染色也同理可以看成代入了\(-1\)的下降幂,也就是某个阶乘乘上一个容斥系数。

有向无环图染色理论得到启发,如果有向无环图能够\(k\)染色,就一定能够\(k+1\)染色。

其实这个染色的容斥原理跟上面题解里面的容斥原理是一样的。

posted on 2020-06-15 09:45  autoint  阅读(404)  评论(0编辑  收藏  举报

导航