Description
用 n n 种颜色给下图中的规则多面体染色,要求第种颜色出现的次数不少于 ci c i ,旋转相同视作同一种方案,问染色方案数
Input
第一行一整数 T T 表示用例组数,每组用例首先输入两个整数表示颜色数量和模数,之后输入 n n 个整数表示对每种颜色使用数量的限制
(1≤T≤1000,1≤n≤60,1≤p<230,0≤ci≤60) ( 1 ≤ T ≤ 1000 , 1 ≤ n ≤ 60 , 1 ≤ p < 2 30 , 0 ≤ c i ≤ 60 )
Output
输出染色方案数,结果模 p p
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.对于穿过两个中心点的六条对称轴,可以旋转,此时每五个菱形颜色需要一样,共 24 24 个变换
2.对于穿过两个尖点的十条对称轴,可以旋转 k⋅π3,k=1,2 k ⋅ π 3 , k = 1 , 2 ,此时每三个菱形颜色需要一样,共 20 20 个变换
3.对于穿过两个中点的十五条对称轴,只能旋转 π2 π 2 ,此时每两个菱形颜色需要一样,共 15 15 个变换
4.不动,此时每个菱形颜色独立,共 1 1 个变换
由定义,染色方案数即为 160(dp(1)+15⋅dp(2)+20⋅dp(3)+24⋅dp(5)) 1 60 ( d p ( 1 ) + 15 ⋅ d p ( 2 ) + 20 ⋅ d p ( 3 ) + 24 ⋅ d p ( 5 ) )
其中 dp(i) d p ( i ) 表示在每种颜色数量满足条件的前提下,使得每 i i 个菱形颜色一样的染色方案数,也即在第种颜色数量不小于 ⌈cji⌉ ⌈ c j i ⌉ 前提下给 60i 60 i 个块染色的方案数,做一个多重背包即可,注意到在运算过程中的模数为 60m 60 m ,计算完括号内的结果后除以 60 60 即为答案,此时由于模数超过 int i n t ,乘法需要注意不要爆 long long l o n g l o n g
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;
}