• [Arc062] Painting Graphs with AtCoDeer


    [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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Android捕捉错误try catch 的简单使用
    ubuntu下安装lua和tolua++
    mosh安装与使用
    三,温习redis持久化解析与配置
    二,温习redis(工具命令使用)
    一,温习Redis (详解从安装到配置)
    报错!-> CPU100%-但是找不到使用cpu的进程
    linux安全---防火墙(iptables)理论解析
    Mysql8.0版二进制安装(my.cnf文件灵活编写)
    ansible实现template管理nginx
  • 原文地址:https://www.cnblogs.com/wxjor/p/9470947.html
Copyright © 2020-2023  润新知