zoukankan      html  css  js  c++  java
  • 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

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

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

    (mle 24)

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

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

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

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

    (nle 15)

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

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

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

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

    记集合幂级数 (F(x)=sum_{Sin 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(qsqrt nlog 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
    ",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
    ",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
    ",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
    ",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(有向无环图)的方案数,两种方案不同当且仅当存在一条边它们的方向不同。

    (nleq 20)

    题解

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

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

    [f(S)=sum_{Tsubset 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
    ",f[n][(1<<n)-1]);
    	return 0;
    }
    

    色多项式

    color_poly

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

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

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

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

  • 相关阅读:
    366. Find Leaves of Binary Tree输出层数相同的叶子节点
    716. Max Stack实现一个最大stack
    515. Find Largest Value in Each Tree Row查找一行中的最大值
    364. Nested List Weight Sum II 大小反向的括号加权求和
    156. Binary Tree Upside Down反转二叉树
    698. Partition to K Equal Sum Subsets 数组分成和相同的k组
    244. Shortest Word Distance II 实现数组中的最短距离单词
    187. Repeated DNA Sequences重复的DNA子串序列
    java之hibernate之基于主键的双向一对一关联映射
    java之hibernate之基于主键的单向一对一关联映射
  • 原文地址:https://www.cnblogs.com/autoint/p/13129266.html
Copyright © 2011-2022 走看看