1815: [Shoi2006]color 有色图
Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 212 Solved: 102
[ Submit][ Status][ Discuss]
Description
Input
输入三个整数N,M,P 1< = N <= 53 1< = M < = 1000 N< P < = 10^ 9
Output
即总数模P后的余数
Sample Input
input 1
3 2 97
3 2 97
Sample Output
output 1
4
4
HINT
Source
题解:置换+dfs
无向完全图如果只考虑点的话,那么就是可以等价成一个点。
因为这道题的置换是点的全排列,那么我们先考虑如何求点的置换。
我们的目标就是求出每个m(f)有多少个取值,然后快速计算,m(f)跟边置换有关,暂且不说,但是数量与点置换相关,我们现在考虑每个m(f)对应的点置换的数量。m(f)是否相同应该与点的划分方式有关(就是拆成几个轮换,以及每个轮换中元素的数量)。计算的时候利用组合数和阶乘。首先如果两个组的元素个数相同,那么在选取的时候先选哪一个都一样,所以需要除去出现次数的阶乘(就是这几个组的全排列)。然后对于组内的元素有(个数-1)的阶乘种排列方式,为啥是个数-1?因为我们要求每个位置都是乱的就是不能再拆成别的轮换。
现在考虑如何计算m(f).
分成两部分考虑:
(1)两个轮换之间的边,形成的轮换的个数是gcd(x,y),其中x,y为轮换中点的数量。
(2)同一轮换中的边,轮换的个数是size/2,我也不是很清楚为什么,只能画画图感受一下(其实就是找规律。。。)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 103
#define LL long long
using namespace std;
int n,q[N],use[N],vis[N],num[N],len,m,k[N];
LL cnt,c[N][N],ans,p,d[N],jc[N],inv[N],a[N],g[N][N],b[N*N];
LL quickpow(LL num,int x)
{
LL ans=1; LL base=num%p;
while (x) {
if (x&1) ans=ans*base%p;
x>>=1;
base=base*base%p;
}
return ans;
}
void init(int n)
{
for (int i=0;i<=n;i++) c[i][0]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;
jc[0]=1;
for (int i=1;i<=n;i++) jc[i]=jc[i-1]*i%p;
for (int i=1;i<=n;i++) inv[i]=quickpow(jc[i],p-2);
b[0]=1;
for (int i=1;i<=n*n;i++) b[i]=b[i-1]*m%p;
}
int gcd(int x,int y){
if (g[x][y]) return g[x][y];
int r;
while (y){
r=x%y;
x=y; y=r;
}
return g[x][y]=x;
}
void dfs(int x,int now)
{
if (!now) {
int n1=x-1; int sum=0;
for (int i=1;i<=n1;i++)
for (int j=i+1;j<=n1;j++) sum+=gcd(a[i],a[j]);
for (int i=1;i<=n1;i++) sum+=a[i]/2;
LL tot=1; int cnt=n;
for (int i=1;i<=n1;i++)
tot=tot*c[cnt][a[i]]%p,cnt-=a[i];
for (int i=1;i<=n;i++)
if (k[i]) tot=tot*inv[k[i]]%p;
for (int i=1;i<=n1;i++) tot=tot*jc[a[i]-1]%p;
ans=(ans+tot*b[sum])%p;
//cout<<tot<<" "<<sum<<endl;
return;
}
for (int i=a[x-1];i<=now;i++) {
k[i]++; a[x]=i;
dfs(x+1,now-i);
k[i]--;
}
}
int main()
{
scanf("%d%d%lld",&n,&m,&p);
if (n==1) {
printf("1\n");
return 0;
}
a[0]=1;
init(53); dfs(1,n);
ans=ans*quickpow(jc[n],p-2)%p;
printf("%lld\n",ans);
}