Description
用nn种颜色给下图中的规则多面体染色,要求第种颜色出现的次数不少于cici,旋转相同视作同一种方案,问染色方案数
Input
第一行一整数TT表示用例组数,每组用例首先输入两个整数表示颜色数量和模数,之后输入nn个整数表示对每种颜色使用数量的限制
(1≤T≤1000,1≤n≤60,1≤p<230,0≤ci≤60)(1≤T≤1000,1≤n≤60,1≤p<230,0≤ci≤60)
Output
输出染色方案数,结果模pp
Sample Input
5
2 1000000007
0 0
2 1000000007
1 0
2 1000000007
0 2
2 1000000007
1 1
5 1000000007
1 1 1 1 1
Sample Output
544393230
544393229
544393228
544393228
905148476
Solution
首先考虑该规则体的旋转变换群,显然对称轴只会穿过十二个中心点(五个菱形的公共点)、或二十个尖点(三个菱形的公共点)、或三十个中点(四个菱形的公共点)
1.对于穿过两个中心点的六条对称轴,可以旋转,此时每五个菱形颜色需要一样,共2424个变换
2.对于穿过两个尖点的十条对称轴,可以旋转k⋅π3,k=1,2k⋅π3,k=1,2,此时每三个菱形颜色需要一样,共2020个变换
3.对于穿过两个中点的十五条对称轴,只能旋转π2π2,此时每两个菱形颜色需要一样,共1515个变换
4.不动,此时每个菱形颜色独立,共11个变换
由定义,染色方案数即为160(dp(1)+15⋅dp(2)+20⋅dp(3)+24⋅dp(5))160(dp(1)+15⋅dp(2)+20⋅dp(3)+24⋅dp(5))
其中dp(i)dp(i)表示在每种颜色数量满足条件的前提下,使得每ii个菱形颜色一样的染色方案数,也即在第种颜色数量不小于⌈cji⌉⌈cji⌉前提下给60i60i个块染色的方案数,做一个多重背包即可,注意到在运算过程中的模数为60m60m,计算完括号内的结果后除以6060即为答案,此时由于模数超过intint,乘法需要注意不要爆long longlong long
Code
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
typedef long double ld;
int T,n,m,d[]={1,2,3,5},num[]={1,15,20,24},sum[5],c[61];
ll mod,C[61][61],dp[61];
ll add(ll x,ll y)
{
x+=y;
if(x>=mod)x-=mod;
return x;
}
ll mul(ll x,ll y)
{
return (x*y-(ll)(x/(ld)mod*y+1e-3)*mod+mod)%mod;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
mod=60ll*m;
for(int i=0;i<=60;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)C[i][j]=add(C[i-1][j-1],C[i-1][j]);
}
memset(sum,0,sizeof(sum));
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];
}
ll ans=0;
for(int i=0;i<4;i++)
{
int nn=60/d[i];
if(sum[i]>nn)continue;
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int j=0;j<n;j++)
{
int cnt=(c[j]+d[i]-1)/d[i];
for(int x=nn;x>=0;x--)
{
ll res=0;
for(int y=cnt;y<=x;y++)
res=add(res,mul(C[x][y],dp[x-y]));
dp[x]=res;
}
}
ans=add(ans,mul(dp[nn],num[i]));
}
ans/=60;
printf("%d\n",(int)ans);
}
return 0;
}