HDU 6360 Kaleidoscope(polya+多重背包)

本文介绍了一种计算规则多面体染色方案数的方法,通过考虑多面体的旋转变换群,利用Polya定理进行计算。文章详细解释了不同对称轴下染色方案的数量,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

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

C722-1011-2

Input

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

(1T1000,1n60,1p<230,0ci60)(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.对于穿过两个中心点的六条对称轴,可以旋转kπ5,k=1,2,3,4,此时每五个菱形颜色需要一样,共2424个变换

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

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

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

Polya定义,染色方案数即为160(dp(1)+15dp(2)+20dp(3)+24dp(5))160(dp(1)+15⋅dp(2)+20⋅dp(3)+24⋅dp(5))

其中dp(i)dp(i)表示在每种颜色数量满足条件的前提下,使得每ii个菱形颜色一样的染色方案数,也即在第j种颜色数量不小于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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值