zoukankan      html  css  js  c++  java
  • 【ARC062F】 Painting Graphs with AtCoDeer 点双连通分量+polya定理

    Description

    给定一张N点M边的无向图,每条边要染一个编号在1到K的颜色。

    你可以对一张染色了的图进行若干次操作,每次操作形如,在图中选择一个简单环(即不经过相同点的环),并且将其颜色逆时针旋转一个单位。

    形式的说,假设你选择的环上的边按顺序依次是e1, e2, ... ,ek,那么经过一次操作后ei mod n+1的颜色会变成操作前ei的颜色。

    两种染色方案被认为是本质相同的,当且仅当其中一种染色后的图经过若干次操作后可以变成另一种染色后的图。

    问有多少本质不同的染色方案,输出对10^9+7取模。

    Input

    第一行三个正整数N M K

    接下来M行每行两个正整数表示图中的一条边。

    Output

    输出一行一个非负整数表示答案。

    Sample Input

    Sample Input 1
    4 4 2
    1 2
    2 3
    3 1
    3 4
    
    Sample Input 2
    5 2 3
    1 2
    4 5
    
    Sample Input 3
    11 12 48
    3 1
    8 2
    4 9
    5 4
    1 6
    2 9
    8 3
    10 8
    4 10
    8 6
    11 7
    1 8
    

    Sample Output

    Sample Output 1
    8
    
    Sample Output 2
    9
    
    Sample Output 3
    569519295
    

    HINT

    1≤N≤50

    1≤M, K≤100

    Sol

    每个点双联通分量的贡献是独立的,我们用tarjan找出每个点双联通分量,然后分类讨论:

    如果只是一条不属于双联通分量的边,那么贡献是k;

    如果是个简单环,那么就转化成项链计数问题,这个要用polya定理来解决,具体地:

    (res=sum_{i=1}^{n}gcd(i,n))

    如果是好多环拼一起的,可以发现分量中任何边都能通过旋转交换,那么我们枚举每个颜色有(x_i)个,这就转化成不定方程解个数,用隔板法解决,即(C_{n+k-1}^{k-1})

    显然第一种和第二种在tarjan中会当成一种处理,而且他们也是等价的。第三种在遇到(low[to[i]]>=dfn[x])的时候判一下,如果有个点被边的出点经过了多次,说明是情况三,然后找出边总数然后计算即可。

    细节:1.反向边记得也要打vis标记 2.当前边也要退栈以及进行计算贡献 3.邻接表下标不要从0开始,如果从0开始的话请把栈数组赋值为-1。

    Code

    #include <bits/stdc++.h>
    #define ll long long
    #define P 1000000007ll
    using namespace std;
    int x,y,n,m,k,I,hed[51],nex[201],to[201],low[201],dfn[201],vis[51],top,s[201],cnt=1,use[201];
    ll ans=1,fac[201],mi[201],ifa[201],inv[201];
    void add(int x,int y){to[++cnt]=y;nex[cnt]=hed[x];hed[x]=cnt;}
    void dfs(int x)
    {
        dfn[x]=low[x]=++I;
        for(int i=hed[x];i!=-1;i=nex[i]) if(!use[i]&&!use[i^1])
        {
            s[++top]=i;use[i]=use[i^1]=1;
            if(!dfn[to[i]])
            {
                dfs(to[i]);low[x]=min(low[x],low[to[i]]);
                if(low[to[i]]>=dfn[x])
                {
                    bool cir=1;int tot=0;ll res=0;
                    for(int j=top;s[j+1]!=i;j--,tot++) if(vis[to[s[j]]]) cir=0;else vis[to[s[j]]]=1;
                    if(cir){for(int j=0;j<tot;j++) res=(res+mi[__gcd(tot,j)])%P;res=res*inv[tot]%P;}
                    else res=fac[tot+k-1]*ifa[k-1]%P*ifa[tot]%P;
                    ans=ans*res%P;
                    for(;s[top+1]!=i;top--) vis[to[s[top]]]=0;
                }
            }
            else low[x]=min(low[x],dfn[to[i]]);
        }
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);mi[0]=fac[0]=fac[1]=ifa[0]=ifa[1]=inv[1]=1;mi[1]=k;memset(hed,-1,sizeof(hed));
        for(int i=2;i<=m+k;i++) mi[i]=mi[i-1]*k%P,fac[i]=i*fac[i-1]%P,inv[i]=(P-P/i)*inv[P%i]%P,ifa[i]=ifa[i-1]*inv[i]%P;
        for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
        for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
        printf("%lld
    ",ans);
    }
    
    
  • 相关阅读:
    JavaScript闭包基本概念
    JavaScript函数
    JavaScript类型比较
    Java思维导图之Class对象
    Python进阶之装饰器
    Java IO学习要点导图
    sl003完全平方数
    sl002个税计算
    sl001数字拼接
    装饰器
  • 原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9469574.html
Copyright © 2011-2022 走看看