題意
給一個聯通無向圖,有n個點和m條邊,要求用k種顏色為其染色,使得相鄰的兩個點顏色不同。n-m≤5,n、m≤100,000。k很大。
思考
若n、m≤8,用最小表示(例如,染色[1,5,3,1]<=>[1,3,2,1])。對於一種染色的最小表示,若使用了m種不同的顏色,最後的結果乘以k!/(k-m)!即可。
考慮到邊數與點數的差很小,嘗試將原圖縮小。
第一種點:度數為1。此時可以直接刪除,最後答案乘以k-1。
第二種點:度數為2。此時可以將與它相鄰的兩個點連起來,並附上新的邊權。最後對於一種暴力的染色方案,相應的邊乘以相應的邊權即可。
這樣邊權就分為兩種,一種是兩端點顏色不同,一種是兩端點顏色相同。我們又知道,對於長度相同的鏈,無論在哪,最後縮成一條邊的貢獻一定是一樣的。
設type0[i]表示兩端點顏色不同,這條邊上有i-1個點(為什麼i-1,因為方便邊權相加)不同的方案數,type1[i]表示兩端點顏色相同,i-1個點不同的方案數。
則
type0[i]=type1[i-1]+type0[i-1]*(k-2)
type1[i]=type1[i-1]*(k-2)+type1[i-2]*(k-1)
初始值
type0[1]=1
type0[2]=k-2
type1[2]=k-1
縮完邊後暴力統計答案。
O(nm3n),這裡n≤10,m≤15。
在腦抽情況下寫出的程式碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long int ll; 4 const ll maxn=1E5+15; 5 const ll mod=1E9+7; 6 ll n,m,k,type0[maxn],type1[maxn],x,y,in[maxn],gg=1,cur,c[maxn],top,back[maxn],ti,ans,what[maxn],vis[maxn]; 7 ll flag; 8 ll inv[maxn]; 9 vector<int>E[maxn]; 10 map<pair<int,int>,int>w; 11 pair<int,int> M(int x,int y) 12 { 13 if(x>y)swap(x,y); 14 return make_pair(x,y); 15 } 16 void erase(vector<int>&S,int x) 17 { 18 for(vector<int>::iterator pt=S.begin();;++pt) 19 if(*pt==x){S.erase(pt);break;} 20 } 21 void add(int u,int v) 22 { 23 E[u].push_back(v); 24 E[v].push_back(u); 25 w[M(u,v)]=1; 26 } 27 void merge(int x,int u,int v) 28 { 29 erase(E[u],x); 30 erase(E[v],x); 31 erase(E[x],u); 32 erase(E[x],v); 33 add(u,v); 34 ll g1=w[M(u,x)],g2=w[M(x,v)]; 35 w[M(u,x)]=w[M(x,v)]=0; 36 w[M(u,v)]=g1+g2; 37 in[x]=0; 38 --cur; 39 } 40 void dfs1(int u) 41 { 42 vis[u]=flag; 43 for(int i=0;i<E[u].size();++i) 44 { 45 int v=E[u][i]; 46 if(vis[v]==flag)continue; 47 dfs1(v); 48 if(in[v]==1) 49 { 50 erase(E[u],v); 51 erase(E[v],u); 52 --in[v],--in[u]; 53 gg=gg*(k-1)%mod; 54 --cur; 55 } 56 } 57 } 58 void paint(int u) 59 { 60 back[u]=++ti; 61 what[ti]=u; 62 vis[u]=1; 63 for(int i=0;i<E[u].size();++i) 64 { 65 int v=E[u][i]; 66 if(vis[v])continue; 67 paint(v); 68 } 69 } 70 ll qpow(ll x,ll y) 71 { 72 ll ans=1,base=x; 73 while(y) 74 { 75 if(y&1)ans=ans*base%mod; 76 base=base*base%mod; 77 y>>=1; 78 } 79 return ans; 80 } 81 void get() 82 { 83 ll sum=gg; 84 for(int u=1;u<=cur;++u) 85 { 86 for(int i=0;i<E[what[u]].size();++i) 87 { 88 int v=E[what[u]][i]; 89 if(u>back[v])continue; 90 if(c[u]==c[back[v]])sum=sum*type1[w[M(what[u],v)]]%mod; 91 else sum=sum*type0[w[M(what[u],v)]]%mod; 92 } 93 } 94 sum=sum*inv[k]%mod*qpow(inv[k-top],mod-2)%mod; 95 ans=(ans+sum)%mod; 96 } 97 void dfs(int s) 98 { 99 if(s==cur+1){get();return;} 100 for(int i=1;i<=top;++i) 101 { 102 c[s]=i; 103 for(int j=0;j<E[what[s]].size();++j) 104 { 105 int v=E[what[s]][j]; 106 if(back[v]>=s)continue; 107 if(c[s]==c[back[v]]&&type1[w[M(what[s],v)]]==0)goto end; 108 else if(type0[w[M(what[s],v)]]==0)goto end; 109 } 110 dfs(s+1); 111 end:; 112 } 113 ++top; 114 c[s]=top; 115 dfs(s+1); 116 --top; 117 } 118 void init() 119 { 120 type0[1]=1; 121 type0[2]=k-2; 122 type1[2]=k-1; 123 for(int i=3;i<=n;++i) 124 { 125 type0[i]=(type1[i-1]%mod+type0[i-1]*(k-2)%mod)%mod; 126 type1[i]=(type1[i-1]*(k-2)%mod+type1[i-2]*(k-1)%mod)%mod; 127 } 128 inv[0]=1; 129 for(int i=1;i<=k;++i)inv[i]=inv[i-1]*i%mod; 130 } 131 int main() 132 { 133 freopen("7_1.in","r",stdin); 134 ios::sync_with_stdio(false); 135 cin>>n>>m>>k; 136 init(); 137 for(int i=1;i<=m;++i) 138 { 139 cin>>x>>y; 140 add(x,y); 141 ++in[x],++in[y]; 142 } 143 cur=n; 144 for(int i=1;i<=n;++i)if(in[i]==1){++flag;dfs1(i);} 145 memset(vis,0,sizeof(vis)); 146 for(int i=1;i<=n&&cur>3;++i) 147 if(in[i]==2&&w[M(E[i][0],E[i][1])]==0)merge(i,E[i][0],E[i][1]); 148 for(int i=1;i<=n;++i) 149 { 150 if(E[i].size()!=0) 151 { 152 paint(i); 153 break; 154 } 155 } 156 dfs(1); 157 cout<<ans<<endl; 158 return 0; 159 }