【纪中集训2019.3.23】染色

题目

描述

一个\(n\)个点\(m\)条边的无向图,求其\(k\)染色的方案数;

染色的方案必须满足对于任意一条边\((u,v)\)\(u\)\(v\)不同色;

答案对\(10^9+7\)取模;

范围

\(n \le 10^5 \ , \ m \le n + 5 , 3 \le k\le 10^5\)

题解

  • 题解说正常的染色问题是\(NPC\)问题,这题可以通过缩点来做;

  • 将图重新定义为有边权\((a,b)\)的图,\(a\)表示两边同色的边权,\(b\)表示两边不同色的边权;

  • 如果让初始时\((a,b)=(0,1)\),那么答案就是每种染色之后所有边权乘积 之和;

  • 考虑去掉一些可以直接计算答案的点\(u\)

    • 度数为\(0\)的点:
      • 去掉之后将答案乘以\(k\);
    • 度数为\(1\)的点\((u,v)\),考虑去掉\(u\)
      • \(k-1\)种情况\(u,v\)同色,\(1\)种情况\(u,v\)异色,所以去掉\(u\)之后答案乘上\(a+(k-1)b\);
    • 度数为\(2\)的点\((u,v_1),(u,v_2)\),考虑去掉\(u\),加入新边\((v_1,v_2)\)
      • \(v1,v2\)同色,有\(k-1\)种情况\(u\)\(v1,v2\)均不同色,\(1\)种情况\(u\)\(v1,v2\)均同色;
        • 新边的\(a\)即:$a \ = \ a_{1}a_{2} + (k-1)b_{1}b_{2} $
      • \(v1,v2\)不同色,有\(k-2\)种情况\(u\)\(v1,v2\)不同色,\(1\)种情况\(u\)仅和\(v1\)同色,\(1\)种情况\(u\)\(和\)\(v1\)同色;
        • 新边的\(b\)即:$b \ = \ a_{1}b_{2} + a_{2}b_{1} + (k-2)b_1a_1 $
    • 在缩图的过程重回产生重边,合并两条重边:
      • \((a,b) = (a_1*a_2 ,b_1b_2 )\)
  • 考虑缩点之后的图不存在度数小于3的节点:

    • \[\begin{cases} 2m \ge 3n \\ m \le n + 5 \\ \end{cases} \]

  • 可以解得\(n \le 10 , m \le 15\);

  • 然后\(Bell(n)m\)枚举划分统计答案即可;

    #include<bits/stdc++.h>
    #define ll long long 
    #define il inline 
    using namespace std;
    const int N=200010,mod=1e9+7;
    int n,m,k,iv[N],fac[N],inv[N],hd[N],o,a[N],b[N],d[N],del[N<<1],vis[N],cnt,U[N],V[N],iE[N],iD[N],bl[N],ans1=1,ans2=0;
    struct Edge{int v,nt;}E[N<<1];
    queue<int>q;
    il char gc(){
    	static char*p1,*p2,s[1000000];
    	if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
    	return(p1==p2)?EOF:*p1++;
    }
    il int rd(){
    	int x=0;char c=gc();
    	while(c<'0'||c>'9')c=gc();
    	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
    	return x;
    }
    il void adde(int u,int v){
    	d[u]++;d[v]++; 
    	E[o]=(Edge){v,hd[u]};hd[u]=o++;
    	E[o]=(Edge){u,hd[v]};hd[v]=o++;
    } 
    const int sz=12345671;
    struct HASH{
    	int o,hd[sz],nt[N];
    	ll val[N];
    	il void init(int i,int u,int v){
    		if(u>v)swap(u,v);
    		ll x=(ll)u*(n+1)+v;
    		nt[++o]=hd[x%sz],hd[x%sz]=o,val[o]=x;
    		adde(u,v);a[i]=0,b[i]=1;
    	}
    	il int find(int u,int v){
    		if(u>v)swap(u,v);
    		ll x=(ll)u*(n+1)+v;
    		for(int i=hd[x%sz];i;i=nt[i])if(val[i]==x){return i;}
    		nt[++o]=hd[x%sz],hd[x%sz]=o,val[o]=x;
    		adde(u,v);a[++m]=1,b[m]=1;
    		return m;
    	}
    }H;
    il void merge(int u){
    	if(!~d[u])return;
    	vis[u]=0;
    	if(!d[u]){
    		d[u]=-1;
    		ans1=(ll)ans1*k%mod;
    	}else if(d[u]==1){
    		int v=0;
    		for(int i=hd[u];~i;i=E[i].nt){
    			if(del[i])continue;
    			del[i]=del[i^1]=1;
    			v=E[i].v;
    			break;
    		}
    		int t=H.find(u,v);
    		ans1=(ll)ans1*(a[t]+(ll)b[t]*(k-1)%mod)%mod;
    		d[u]=-1;--d[v];
    		if(d[v]<=2&&!vis[v]){vis[v]=1;q.push(v);}
    	}else{
    		int v1=0,v2=0;
    		for(int i=hd[u];~i;i=E[i].nt){
    			if(del[i])continue;
    			del[i]=del[i^1]=1;
    			if(!v1){v1=E[i].v;continue;}
    			v2=E[i].v;break;
    		}
    		int t=H.find(v1,v2),t1=H.find(u,v1),t2=H.find(u,v2);
    		a[t]=(ll)a[t] * ((ll)(k-1)*b[t1]%mod*b[t2]%mod + (ll)a[t1]*a[t2]%mod)%mod;
    		b[t]=(ll)b[t] * ((ll)(k-2)*b[t1]%mod*b[t2]%mod + (ll)a[t1]*b[t2]%mod + (ll)b[t1]*a[t2]%mod)%mod; 
    		d[u]=-1;--d[v1];--d[v2];
    		if(d[v1]<=2&&!vis[v1]){vis[v1]=1;q.push(v1);}
    		if(d[v2]<=2&&!vis[v2]){vis[v2]=1;q.push(v2);}
    	}
    }
    il void cal(int y){
    	int re=1;
    	for(int i=1;i<=m;++i){
    		if(bl[U[i]]==bl[V[i]])re=(ll)re*a[iE[i]]%mod;
    		else re=(ll)re*b[iE[i]]%mod;
    	}
    	re=(ll)re*fac[k]%mod*inv[k-y]%mod;
    	ans2=(ans2+re)%mod;
    }
    void dfs(int x,int y){
    	if(y>k)return;
    	if(x==n+1){cal(y);return;}
    	for(int i=1;i<=y+1;++i){
    		bl[x]=i;
    		dfs(x+1,max(i,y));
    	}
    }
    int main(){
    	freopen("color.in","r",stdin);
    	freopen("color.out","w",stdout);
    	n=rd();m=rd();k=rd();
    	for(int i=1;i<=n;++i)hd[i]=-1;
    	iv[1]=1;for(int i=2;i<=k;++i)iv[i]=(ll)(mod-mod/i)*iv[mod%i]%mod;
    	for(int i=fac[0]=inv[0]=1;i<=k;++i){
    		fac[i]=(ll)fac[i-1]*i%mod;
    		inv[i]=(ll)inv[i-1]*iv[i]%mod;
    	}
    	for(int i=1;i<=m;++i){
    		int u=rd(),v=rd();
    		H.init(i,u,v);
    		H.find(u,v);
    	}
    	for(int i=1;i<=n;++i)if(d[i]<=2)q.push(i),vis[i]=1;
    	while(!q.empty())
    	merge(q.front()),q.pop();
    	int tmp=0;for(int i=1;i<=n;++i)if(~d[i])iD[i]=++tmp;
    	n=tmp;tmp=0;
    	for(int i=1;i<=m;++i)if(!del[(i-1)<<1]){
    		int u=E[(i-1)<<1].v,v=E[(i-1)<<1|1].v;
    		++tmp;u=iD[u];v=iD[v];
    		U[tmp]=u;V[tmp]=v;iE[tmp]=i;
    	}
    	m=tmp;dfs(1,0);
    	cout<<(ll)ans1*ans2%mod<<endl;
    	return 0;
    }
    
posted @ 2019-03-24 17:19  大米饼  阅读(321)  评论(0)    收藏  举报