Description
用 种颜色给下图中的规则多面体染色,要求第 种颜色出现的次数不少于 ,旋转相同视作同一种方案,问染色方案数
Input
第一行一整数 表示用例组数,每组用例首先输入两个整数 表示颜色数量和模数,之后输入 个整数 表示对每种颜色使用数量的限制
Output
输出染色方案数,结果模
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.对于穿过两个中心点的六条对称轴,可以旋转 ,此时每五个菱形颜色需要一样,共 个变换
2.对于穿过两个尖点的十条对称轴,可以旋转 ,此时每三个菱形颜色需要一样,共 个变换
3.对于穿过两个中点的十五条对称轴,只能旋转 ,此时每两个菱形颜色需要一样,共 个变换
4.不动,此时每个菱形颜色独立,共 个变换
由 定义,染色方案数即为
其中 表示在每种颜色数量满足条件的前提下,使得每 个菱形颜色一样的染色方案数,也即在第 种颜色数量不小于 前提下给 个块染色的方案数,做一个多重背包即可,注意到在运算过程中的模数为 ,计算完括号内的结果后除以 即为答案,此时由于模数超过 ,乘法需要注意不要爆
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;
}