HDU 6360 2018HDU多校赛第五场 kaleidoscope(Polya计数+dp)

大致题意:一个菱形六面体,有60个面,然后每个面进行染色,然后要求是第i种颜色不少于c[i]个,问有多少个本质不同的染色方案。

看到这个菱形六面体,60个面,不要自闭……其实仔细想想这个图形也很简单。我们把每一个凸出来的菱形顶点相连,我们发现会变成一个十二面体。正如题目种所说,菱形六面体是十二面体的每个面中点往中间收缩形成的。因此,这个60面看起来吓人,但其实就是一个十二面体。

所以对于十二面体,我们同样考虑用polya计数。根据套路,首先计算定理和置换数,然后计算每一个置换的循环节,还有每一类置换群对应的循环个数。对于同一个循环里面的东西,我们要让它颜色相同,那么相当于对每种置换群求有多少个循环,用循环个数对应不考虑旋转的答案。把所有置换群的答案计算求和之后,除以总置换数就是最后的结果。

现在,我们来考虑一下正十二面体有多少种置换。在脑海里想(bai)象(du)一下十二面体的图形。

                                                 

很显然,以相对面面的中心连线为轴进行旋转,分别转108度、216度、324度和432度,对应有12/2*4=24种置换。

以相对的两个顶点的连线为轴进行旋转,分别转120度和240度,对应有20/2*2=20种置换。

以相对的两条棱的中点连线为轴,旋转180度,对应有30/2*1=15种置换。

扫描二维码关注公众号,回复: 2865514 查看本文章

静止不动总共有1种置换。

 

综上,总的置换数目是24+20+15+1=60种,有四个置换群,对应的循环节长度分别是5、3、2、1,对应的循环个数就是12、20、30和60。然后就是在不考虑旋转的情况下,12、20、30和60个面,在满足限制的条件下有多少种涂色方案。这个我们考虑用dp。令dp[i][j]表示用前i个颜色涂前j个面的方案数,那么有转移方程:

                                            \large dp[i][j]=\sum_{k=c[i]}^{j}dp[i-1][j-k]*C_{j}^{k}

这个c[i]表示第i种颜色的最低次数限制。然后还有一个问题,根据polya定理,最后答案还有除以置换数,但是在取模意义下原本保证能够整除的定理不一定能够整除,而这个模数不一定是质数,不一定存在逆元。所以,根据tls所说,这里我可以把模数乘以这个除数,这样在取模意义下,最后结果还是能够整除除数的。然后中间在想成的过程中可能会爆LL,所以用到快速乘,这里快速乘我也学了tls,一个数字拆成两个部分……具体见代码:

#include<bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define LL long long
#define N 100010
using namespace std;

const LL BLEN=25,BMSK=(1<<25)-1;
LL t[4]={1,15,20,24},len[4]={1,2,3,5},c[61][61];
LL dp[100][100],a[100],s[4],mod;

LL multiply(LL x,LL y,LL mo)
{
    LL x1=x>>BLEN,y1=y>>BLEN;
    LL x2=x&BMSK,y2=y&BMSK;
    LL res=x1&&y1?(((x1*y1%mo)<<BLEN)%mo<<BLEN)%mo:0;
    res=(res+(x1?((x1*y2%mo)<<BLEN)%mo:0))%mo;
    res=(res+(y1?((x2*y1%mo)<<BLEN)%mo:0))%mo;
    return (res+y2*x2%mo)%mo;
}

int main()
{
    file(1011);
    IO;
    int T,n; cin>>T;
    for(int i=0;i<=60;i++) c[i][0]=1;
    while(T--)
    {
        cin>>n>>mod;
        LL ans=0,mo=mod*60;
        memset(s,0,sizeof(s));
        for(int i=1;i<=60;i++)
            for(int j=0;j<=i;j++)
                c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            for(int j=0;j<4;j++)
                s[j]+=a[i]?(a[i]-1)/len[j]+1:0;
        }
        for(int i=0;i<4;i++)
        {
            int up=60/len[i];
            if (up<s[i]) continue;
            memset(dp,0,sizeof(dp));
            dp[0][0]=1;
            for(int j=1;j<=n;j++)
            {
                int down=a[j]?(a[j]-1)/len[i]+1:0;
                for(int k=down;k<=up;k++)
                    for(int l=down;l<=k;l++)
                        dp[j][k]=(dp[j][k]+multiply(dp[j-1][k-l],c[k][l],mo))%mo;
            }
            ans=(ans+multiply(t[i],dp[n][up],mo))%mo;
        }
        assert(ans%60==0);
        cout<<ans/60<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/81488475