• P4128 [SHOI2006]有色图


    传送门

    数学渣渣看题解看得想死Ծ‸Ծ

    首先发现这玩意儿看着很像polya定理

    [L=frac{1}{|G|}sum_{iin G}m^{w(i)} ]

    然而polya定理只能用来求点的置换,边的置换是布星的

    于是我们考虑一个点的置换,把它写成若干循环的乘积((a_1,a_2,..)(b_1,b_2,...)...)

    1.对于不在同一个循环里的点,比方说一条边((a_1,b_1)),那么和它在同一个循环的边有(((a_1,b_1),(a_2,b_2),...))(a)的循环节为(l_1)(b)的循环节为(l_2),那么这个边的循环的循环节长度就是(lcm(l_1,l_2)),而总共的边数为(l_1*l_2),那么循环的个数就是(frac{l_1*l_2}{lcm(l_1,l_2)}=gcd(l_1,l_2))

    2.对于在同一个循环内的点,设(a)的循环节长度为(l_1)

    如果(l_1)长度为奇数,那么循环的长度就是(l_1),总共有(C_{l_1}^{2})条边,那么循环的个数就是(frac{l-1}2)

    如果(l_1)长度为偶数,除了上面的情况之外,还有一种很gg的情况就是比方说((a_1,a_{l_1/2+1}))的边所在的循环,这个循环的长度是(l_1/2),占的边数为(l_1/2),除此之外其他的情况都是一样的,所以循环的个数就是(frac{frac{l(l-1)}{2}-frac{l}2}{l}+1=frac l 2)

    那么,总共的边的置换就是

    [c=sum_{i=1}^klfloorfrac{l_i}{2} floor+sum_{i=1}^ksum_{j=i+1}^ngcd(l_i,l_j) ]

    然而如果直接枚举的话复杂度是带一个感叹号的……然而我们只需要知道所有的(l_i)就行了,而不需要知道循环里具体是什么数字……所以会T就是我们知道的太多了

    于是我们可以枚举(l_i),为了不重不漏保证(l_i)不降,先考虑如果(l_i)互不相同的话有多少种方案。我们可以这样理解,枚举(n)个数字的全排列,然后按(l_i)从左到右依次分组,那么这样肯定就是一个置换了……然后对于其中的每一个循环((a_1,a_2,...,a_l))来说,((a_2,a_3,...,a_l,a_1)...)之类的其实是跟它一样的,也就是说每个循环有(l_i)个同构的,所以要除掉,那么方案数就是

    [S=frac{n!}{prod_{i=1}^k l_i} ]

    然而现在问题是(l_i)有可能会相等,如果按上面那样考虑的话有可能会有两个(l_i)相等的循环被算到不同的里面了……所以还要设(B_i)(l_j==i)的个数,然后除掉他们中间排列的个数,那么方案数应该是

    [S=frac{n!}{prod_{i=1}^k l_i*B_i!} ]

    然后dfs暴力找(l_i)即可

    最后就是

    [Ans=Ans+S*m^c ]

    之后就是抄代码了

    //minamoto
    #include<bits/stdc++.h>
    #define int long long
    #define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
    #define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
    using namespace std;
    const int N=105;
    int ans,P,n,m,fac[N],rec[N];
    int gcd(int x,int y){return y?gcd(y,x%y):x;}
    int ksm(int x,int y){
    	int res=1;
    	for(;y;y>>=1,x=x*x%P)if(y&1)res=res*x%P;
    	return res;
    }
    void calc(int x){
    	int sum=0,mul=1,now=1;
    	fp(i,1,x)sum+=rec[i]/2;
    	fp(i,1,x)fp(j,i+1,x)sum+=gcd(rec[i],rec[j]);
    	fp(i,1,x)(mul*=rec[i])%=P;
    	fp(i,2,x){
    		if(rec[i]!=rec[i-1])(mul*=fac[now])%=P,now=0;
    		++now;
    	}(mul*=fac[now])%=P,mul=fac[n]*ksm(mul,P-2)%P;
    	(ans+=mul*ksm(m,sum)%P)%=P;
    }
    void dfs(int k,int x,int s){
    	if(!x)calc(k-1);if(x<s)return;
    	fp(i,s,x)rec[k]=i,dfs(k+1,x-i,i);
    }
    signed main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%lld%lld%lld",&n,&m,&P),fac[0]=1;
    	fp(i,1,n)fac[i]=fac[i-1]*i%P;
    	dfs(1,n,1);(ans*=ksm(fac[n],P-2))%=P;
    	printf("%lld
    ",ans);return 0;
    }
    
  • 相关阅读:
    [Effective JavaScript 笔记]第47条:绝不要在Object.prototype中增加可枚举的属性
    [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合
    [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
    [Effective JavaScript 笔记]第44条:使用null原型以防止原型污染
    redhat下配置SEED DVS6446开发环境3
    redhat下配置SEED DVS6446开发环境2
    redhat下配置SEED DVS6446开发环境1
    关于[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] 的解释
    SQL2008附加数据库报错
    结构体与类
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10054795.html
Copyright © 2020-2023  润新知