HDU 6360 Kaleidoscope(polya+多重背包)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/82453412

Description

n 种颜色给下图中的规则多面体染色,要求第 i 种颜色出现的次数不少于 c i ,旋转相同视作同一种方案,问染色方案数

C722-1011-2

Input

第一行一整数 T 表示用例组数,每组用例首先输入两个整数 n , p 表示颜色数量和模数,之后输入 n 个整数 c i 表示对每种颜色使用数量的限制

( 1 T 1000 , 1 n 60 , 1 p < 2 30 , 0 c i 60 )

Output

输出染色方案数,结果模 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.对于穿过两个中心点的六条对称轴,可以旋转 k π 5 , k = 1 , 2 , 3 , 4 ,此时每五个菱形颜色需要一样,共 24 个变换

2.对于穿过两个尖点的十条对称轴,可以旋转 k π 3 , k = 1 , 2 ,此时每三个菱形颜色需要一样,共 20 个变换

3.对于穿过两个中点的十五条对称轴,只能旋转 π 2 ,此时每两个菱形颜色需要一样,共 15 个变换

4.不动,此时每个菱形颜色独立,共 1 个变换

P o l y a 定义,染色方案数即为 1 60 ( d p ( 1 ) + 15 d p ( 2 ) + 20 d p ( 3 ) + 24 d p ( 5 ) )

其中 d p ( i ) 表示在每种颜色数量满足条件的前提下,使得每 i 个菱形颜色一样的染色方案数,也即在第 j 种颜色数量不小于 c j i 前提下给 60 i 个块染色的方案数,做一个多重背包即可,注意到在运算过程中的模数为 60 m ,计算完括号内的结果后除以 60 即为答案,此时由于模数超过 i n t ,乘法需要注意不要爆 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;
}

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/82453412