[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-1choose 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)$,可以通过

 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 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14462860.html