1. 程式人生 > >[Arc062] Painting Graphs with AtCoDeer

[Arc062] Painting Graphs with AtCoDeer

方案 == script main esp 不同 bool href 簡述

[Arc062] Painting Graphs with AtCoDeer

Description

給定一張N點M邊的無向圖,每條邊要染一個編號在1到K的顏色。你可以對一張染色了的圖進行若幹次操作,每次操作形如,在圖中選擇一個簡單環(即不經過相同點的環),並且將其顏色逆時針旋轉一個單位。形式的說,假設你選擇的環上的邊按順序依次是e1, e2, ... ,ek,那麽經過一次操作後ei mod n+1的顏色會變成操作前ei的顏色。兩種染色方案被認為是本質相同的,當且僅當其中一種染色後

的圖經過若幹次操作後可以變成另一種染色後的圖。問有多少本質不同的染色方案,輸出對109+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

試題分析

首先對於每條不在任何環中的邊,其貢獻是\(K\)

倍,因為它可以染任意顏色並且不與其它邊交換。
那麽剩下的就是若幹個邊互不相交的極大點雙聯通分量,肯定分別來求。
考慮一個大小為4的環,中間對角線有一條邊,那麽猜想它的任意兩條邊都可以交換。
分類討論證明,即證明如上描述圖中的一條長度為3的鏈,可以任意像大小為3的環那樣交換,並證明如上描述圖中4環上的一條邊能與對角線交換,即可證明可以隨意交換。
但是還剩下“裸環”的情況,這就是裸的\(Polya\)定理了,簡述一下\(Polya\)定理: \[Ans=\frac{1}{|G|}(m^{c(f_1)}+m^{c(f_2)} \ldots m^{c^{f_G}})\]
其中\(c(x)\)即為循環個數,由\(Burnside\)
引理中的“不動點”個數求出,“不動點”個數即為循環個數乘上顏色數,因為循環各個獨立,只要保證一個循環中所有點都是同種顏色的即可。
另外,\(c(x)=gcd(i,G)\),證明請見:link
對於點雙聯通分量不是“裸環”的情況組合數隔板法即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#include<algorithm>

using namespace std;
#define LL long long

inline LL read(){
    LL x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 100010;
const LL Mod = 1e9+7;

LL N,M,K; LL cnt;
struct edge{
    LL u,v; edge(LL uu=0,LL vv=0){
        u=uu; v=vv;
    }
}edg[MAXN+1];
LL fac[MAXN+1],ifac[MAXN+1],inv[MAXN+1];
LL Node[MAXN<<1],Next[MAXN<<1],Root[MAXN<<1];
LL sz[MAXN+1]; bool inq[MAXN+1];
vector<edge> vec[MAXN+1];
LL fa[MAXN+1]; bool vis[MAXN+1];
LL tim,col,top; LL q[MAXN+1];

inline LL Pow(LL a,LL b){
    LL res=1; for(;b;b>>=1,a=a*a%Mod) if(b&1) res=res*a%Mod; return res;
}
inline void insert(LL u,LL v){
    Node[++cnt]=v; Next[cnt]=Root[u]; Root[u]=cnt; return ; 
} LL dfn[MAXN+1],low[MAXN+1],sta[MAXN+1];
LL ans=1;

inline LL gcd(LL a,LL b){
    if(!b) return a; return gcd(b,a%b);
}
inline LL Polya(LL sz){
    //cout<<"Polya:"<<sz<<endl;
    LL res=0; for(LL i=1;i<=sz;i++) (res+=Pow(K,gcd(i,sz)))%=Mod;
    return res*Pow(sz,Mod-2)%Mod;
}
inline LL C(LL n,LL m){
    if(n<m) return 0; if(!m) return 1;
    return fac[n]*ifac[m]%Mod*ifac[n-m]%Mod;
}
inline void dfs(LL k,LL fa){
    dfn[k]=low[k]=++tim; sta[++top]=k;
    for(LL x=Root[k];x;x=Next[x]){
        LL v=Node[x]; 
        if(v==fa) continue;
        if(!dfn[v]){
            dfs(v,k); low[k]=min(low[k],low[v]);
            if(dfn[k]<=low[v]){
                LL tp=0,sz=0; ++col;
                memset(vis,false,sizeof(vis));
                while(q[tp]!=v){
                    vis[sta[top]]=1; q[++tp]=sta[top]; --top;
                } vis[k]=1; q[++tp]=k;
                for(LL j=1;j<=tp;j++)
                    for(LL x1=Root[q[j]];x1;x1=Next[x1])
                        if(vis[Node[x1]]) ++sz;
                sz>>=1;
                if(sz<tp) ans=ans*K%Mod;
                else if(tp==sz) ans=ans*Polya(sz)%Mod;
                else ans=ans*C(sz+K-1,K-1)%Mod;
            }
        } else {
            low[k]=min(low[k],dfn[v]);
        }
    } return ;
} 

int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    N=read(),M=read(),K=read(); //LL ans=1; 
    fac[0]=1;
    for(LL i=1;i<=MAXN;i++) fac[i]=fac[i-1]*i%Mod;
    inv[1]=ifac[0]=ifac[1]=1;
    for(LL i=2;i<=MAXN;i++) inv[i]=(Mod-(Mod/i))*inv[Mod%i]%Mod,ifac[i]=ifac[i-1]*inv[i]%Mod;
    for(LL i=1;i<=M;i++){
        LL u=read(),v=read();
        insert(u,v); insert(v,u);
        edg[i].u=u; edg[i].v=v; //E[u][v]=E[v][u]=true;
    }
    for(LL i=1;i<=N;i++)
        if(!dfn[i]) dfs(i,0);
    printf("%lld\n",ans);
    return 0;
}

[Arc062] Painting Graphs with AtCoDeer