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)染色。

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

  • 相关阅读:
    4、线程--线程同部
    3、线程--线程调度
    2、线程--线程之前的状态转换
    1、线程--Thread&Runnable创建线程
    5、JUC--实现 Callable 接口
    13、JUC--ForkJoinPool 分支/合并框架 工作窃取
    linux连接iscsi存储方法
    ORA-01031: insufficient privileges 错误解决
    【opatch打补丁】oracle10.2.0.5.0升级10.2.0.5.9 for linux
    错误 ORA-01102: cannot mount database in EXCLUSIVE mode 的处理方法
  • 原文地址:https://www.cnblogs.com/autoint/p/13129266.html
Copyright © 2011-2022 走看看