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);
    }
    
    
  • 相关阅读:
    7月15日考试 题解(链表+状压DP+思维题)
    暑假集训日记
    C# .NET 使用 NPOI 生成 .xlsx 格式 Excel
    JavaSE 基础 第42节 局部内部类
    JavaSE 基础 第41节 匿名内部类
    JavaSE 基础 第40节 内部类概述
    JavaSE 基础 第39节 接口的应用
    JavaSE 基础 第38节 接口的实现
    JavaSE 基础 第37节 接口概述
    JavaSE 基础 第36节 抽象类概述与使用
  • 原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9469574.html
Copyright © 2011-2022 走看看