版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/82946121
【题目】
原题地址
一个正十二面体,用
种颜色涂色,第
种颜色至少要用
次,求方案数。
【解题思路】
polya好题。
首先写出所有置换。
不动:
种
对棱中点连线旋转:
种
对顶点连线旋转:
种
对面中点连线旋转:
种
接下来我们需要证明这是一个置换群。
然而这个东西实在是太难搞了,我们只能YY一下,它就是一个群。
那么现在四个置换群循环节长度分别为
,对应循环节个数
也就是说现在我们要计算的就是染完每种循环的方案数,这个用一个背包就可以解决。
令
表示前
种颜色,染了
个循环节的方案数,那么我们有:
一个问题是模数不一定是素数,那最后除法除不尽。
我们可以将模数先乘以
,这样计算除的结果一定是整除
的,最后再将答案除以
即可,需要使用快速乘。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ldb;
const int N=65;
const ldb eps=1e-8;
int T,n,m;
int sum[5],c[N],d[]={1,2,3,5},num[]={1,15,20,24};
LL mod,ans,C[N][N],f[N];
void up(LL &x,LL y) {x+=y;if(x>=mod)x-=mod;}
LL upm(LL x) {return x>=mod?x-mod:x;}
LL mul(LL x,LL y) {return (x*y-(LL)(x/(ldb)mod*y+eps)*mod+mod)%mod;}
int main()
{
#ifndef ONLINE_JUDGE
freopen("HDU6360.in","r",stdin);
freopen("HDU6360.out","w",stdout);
#endif
scanf("%d",&T);
while(T--)
{
memset(sum,0,sizeof(sum));ans=0;
scanf("%d%d",&n,&m);mod=(LL)m*60;
for(int i=0;i<=60;++i)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;++j) C[i][j]=upm(C[i-1][j-1]+C[i-1][j]);
}
for(int i=0;i<n;++i)
{
scanf("%d",&c[i]);
for(int j=0;j<4;++j) sum[j]+=(c[i]+d[j]-1)/d[j];
}
for(int i=0;i<4;++i)
{
int tn=60/d[i];
if(sum[i]>tn) continue;
memset(f,0,sizeof(f));f[0]=1;
for(int j=0;j<n;++j)
{
int cnt=(c[j]+d[i]-1)/d[i];
for(int k=tn;~k;--k)
{
LL res=0;
for(int l=cnt;l<=k;++l) up(res,mul(C[k][l],f[k-l]));
f[k]=res;
}
}
up(ans,mul(f[tn],num[i]));
}
ans/=60;printf("%lld\n",ans%mod);
}
return 0;
}
【总结】
我说它是一个群,它就是一个群。