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

HDU 6360 2018HDU多校赛第五场 kaleidoscope(Polya计数+dp)_第1张图片

 

 

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

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

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

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

                                                  HDU 6360 2018HDU多校赛第五场 kaleidoscope(Polya计数+dp)_第2张图片

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

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

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

静止不动总共有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
#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)<>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

 

你可能感兴趣的:(---------Online,Judge--------,HDU,2018HDU多校赛,Polya,背包dp)