您的位置:首页 > 大数据 > 人工智能

[ARC062]F - Painting Graphs with AtCoDeer 双联通分量+Pólya计数

2018-01-24 21:55 253 查看
题面

把边分成三种:

1. 不在点双联通分量中,直接贡献KK。

2. 在一个环中,记环长为nn,根据Pólya定理,其贡献为1|G|∑g∈GKc(g)=1n∑i=0n−1Kgcd(i,n)(205)(205)1|G|∑g∈GKc(g)=1n∑i=0n−1Kgcd(i,n)

3. 在一个由多个环重叠的点双联通分量中,可以证明可以通过旋转来交换任意两条边的颜色,于是本质不同当且仅当有一种颜色数量不同,其贡献为(n+K−1K−1)(206)(206)(n+K−1K−1)

其实1,2两种情况可以一起考虑。

发现原来并没有写过真正的点双,要么是求割点,要么是边双缩点,要么是仙人掌那种只有环的。。。所以有一下几个要注意的:

1. 显然一个边最多只能在一个点双中,栈中记录的当然是边,但有的边可能正反两次被记录,所以还要记个vis数组。

2. 为了严谨(虽然这题没有重边),但还是最好记录从哪条边来的而不是从哪个点来的。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define chkmin(x,y) x=min(x,y)
#define ll long long
using namespace std;
const int mod=1000000007;
int n,m,k,tim,top=0,low[55],dfn[55];
bool vis[55];
ll mi[210],ans=1,fac[210],ifac[210],inv[210];
struct edge
{
int t;bool vis;
edge *next,*rev;
}*con[55],*st[110];
void ins(int x,int y)
{
edge *p=new edge;
p->t=y;p->vis=0;
p->next=con[x];
con[x]=p;
}
ll ksm(ll a,int b){ll r=1;for(;b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
void tarjan(int v,edge *fro)
{
dfn[v]=low[v]=++tim;
for(edge *p=con[v];p;p=p->next)
if(!p->vis&&!p->rev->vis)
{
st[++top]=p;p->vis=p->rev->vis=1;
if(!dfn[p->t])
{
tarjan(p->t,p);
chkmin(low[v],low[p->t]);
if(low[p->t]>=dfn[v])
{
bool cir=1;int cnt=0;ll cal=0;
for(int i=top;st[i+1]!=p;cnt++,i--)
if(vis[st[i]->t]) cir=0;
else vis[st[i]->t]=1;
if(cir)
{
for(int i=0;i<cnt;i++)
cal=(cal+mi[gcd(cnt,i)])%mod;
cal=cal*inv[cnt]%mod;
}
else cal=fac[cnt+k-1]*ifac[cnt]%mod*ifac[k-1]%mod;
ans=ans*cal%mod;
for(;st[top+1]!=p;top--) vis[st[top]->t]=0;
}
}
else chkmin(low[v],dfn[p->t]);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
mi[0]=1;
for(int i=1;i<=m;i++)
mi[i]=mi[i-1]*k%mod;
fac[0]=1;
for(int i=1;i<=m+k;i++)
fac[i]=fac[i-1]*i%mod;
ifac[m+k]=ksm(fac[m+k],mod-2);

for(int i=m+k-1;i>=0;i--)
ifac[i]=ifac[i+1]*(i+1)%mod;
for(int i=1;i<=m+k;i++)
inv[i]=ifac[i]*fac[i-1]%mod;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
con[x]->rev=con[y];con[y]->rev=con[x];
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i,NULL);
printf("%lld",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: