[atARC062F]Painting Graphs with AtCoDeer
求出点双后缩点,对于点双之间,显然不存在简单环,即每一个简单环一定在一个点双内部,换言之即每一个点双可以独立的考虑,然后将结果相乘
(对于点双之间的边任意染色,即若有$s$条边,还会有$k^{s}$的贡献)
对点双分类讨论(假设其有$n$个节点,$m$条边):
1.$n=2$且$m=1$(也就是两点一边),贡献为$k$
2.$n=m$(一个环),根据polya定理,贡献即$\frac{\sum_{i=0}^{n-1}k^{\gcd(n,i)}}{n}$
3.$n<m$,则任意两边的颜色都可以单独交换(其他边的颜色不变),即仅关心每种颜色的边的数量,根据插板法,贡献即${m+k-1\choose k-1}$
关于第3类中任意两边的颜色可以单独交换,下面来证明一下:
更方便的,由于整个连通,那么原结论等价于可以单独交换有公共端点的两条边
简单分析,可以发现这两条边必然会属于一个简单环$R_{1}$,且$R_{1}$与另一个简单环$R_{2}$有公共边
先通过在$R_{1}$上轮换,使得其中恰好有一条边属于公共边的部分,另一条边仅属于$R_{1}$
接下来,将这两条边单独交换,然后再轮换返回原来的位置
具体来说,先轮换$R_{2}$使得原本在公共边上的边仅属于$R_{2}$,再轮换$R_{1}$使得原本在$R_{1}$中的边在公共边上,再轮换$R_{1}$和$R_{2}$除去公共边的部分,可以发现就完成了交换
(更形象地,可以参考atcoder题解中第3页的4幅图)
时间复杂度为$o(n+m)$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 55 4 #define mod 1000000007 5 struct Edge{ 6 int nex,to; 7 }edge[N<<2]; 8 stack<int>st; 9 vector<int>v[N]; 10 int bcc,E,n,m,k,x,y,ans,head[N],dfn[N],low[N],vis[N]; 11 int gcd(int x,int y){ 12 if (!y)return x; 13 return gcd(y,x%y); 14 } 15 int pow(int n,int m){ 16 int s=n,ans=1; 17 while (m){ 18 if (m&1)ans=1LL*ans*s%mod; 19 s=1LL*s*s%mod; 20 m>>=1; 21 } 22 return ans; 23 } 24 void add(int x,int y){ 25 edge[E].nex=head[x]; 26 edge[E].to=y; 27 head[x]=E++; 28 } 29 void dfs(int k,int fa){ 30 if (fa)st.push(k); 31 dfn[k]=low[k]=++dfn[0]; 32 for(int i=head[k];i!=-1;i=edge[i].nex) 33 if (edge[i].to!=fa){ 34 if (dfn[edge[i].to])low[k]=min(low[k],dfn[edge[i].to]); 35 else{ 36 dfs(edge[i].to,k); 37 low[k]=min(low[k],low[edge[i].to]); 38 if (low[edge[i].to]>=dfn[k]){ 39 while (1){ 40 v[bcc].push_back(st.top()); 41 st.pop(); 42 if (v[bcc].back()==edge[i].to)break; 43 } 44 v[bcc++].push_back(k); 45 } 46 } 47 } 48 } 49 int main(){ 50 scanf("%d%d%d",&n,&m,&k); 51 memset(head,-1,sizeof(head)); 52 for(int i=1;i<=m;i++){ 53 scanf("%d%d",&x,&y); 54 add(x,y); 55 add(y,x); 56 } 57 for(int i=1;i<=n;i++) 58 if (!dfn[i])dfs(i,0); 59 ans=1; 60 for(int i=0;i<bcc;i++){ 61 int nn=v[i].size(),mm=0,s=0; 62 memset(vis,0,sizeof(vis)); 63 for(int j=0;j<v[i].size();j++)vis[v[i][j]]=1; 64 for(int j=0;j<v[i].size();j++) 65 for(int k=head[v[i][j]];k!=-1;k=edge[k].nex) 66 if (vis[edge[k].to])mm++; 67 mm/=2; 68 if ((nn==2)&&(mm==1))s=k; 69 else{ 70 if (nn==mm){ 71 for(int j=0;j<nn;j++)s=(s+pow(k,gcd(nn,j)))%mod; 72 s=1LL*s*pow(nn,mod-2)%mod; 73 } 74 else{ 75 s=1; 76 for(int j=mm+1;j<mm+k;j++)s=1LL*s*j%mod; 77 for(int j=1;j<k;j++)s=1LL*s*pow(j,mod-2)%mod; 78 } 79 } 80 ans=1LL*ans*s%mod; 81 } 82 printf("%d",ans); 83 }