BZOJ1004 HNOI2008Cards(Burnside引理+动态规划)

  直接给了一个置换群(当然要自己手动加上不洗牌的情况)。考虑求不动点数量即可。对于一个置换,求出所有循环的长度,然后设f[i][x][y]为给前i个循环着色后,用了x张红色卡片、y张绿色卡片的方案数,dp一发即可。

  upd:为啥我写的应该不是假算法却好像也被hack掉了?不管了已经忘了这是啥题肯定哪写挂了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 66
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,A,B,C,m,P,a[N],cycle[N],f[N][N][N],ans;
bool flag[N];
int calc()
{
    memset(f,0,sizeof(f));f[0][0][0]=1;int s=0;
    for (int j=1;j<=n;j++)
    {
        s+=cycle[j];
        for (int x=0;x<=A;x++)
            for (int y=0;y<=B;y++)
            {
                int z=s-x-y;if (z>C) continue;
                if (x>=cycle[j]) f[j][x][y]+=f[j-1][x-cycle[j]][y];
                if (y>=cycle[j]) f[j][x][y]+=f[j-1][x][y-cycle[j]];
                if (z>=cycle[j]) f[j][x][y]+=f[j-1][x][y];
                f[j][x][y]%=P;
            }
    }
    return f[n][A][B];
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj1004.in","r",stdin);
    freopen("bzoj1004.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    A=read(),B=read(),C=read(),m=read(),P=read();
    for (int i=1;i<=m;i++)
    {
        for (int j=1;j<=n;j++)
        a[read()]=j;
        memset(flag,0,sizeof(flag));n=0;
        for (int j=1;j<=n;j++)
        if (!flag[j])
        {
            int x=a[j];flag[j]=1;cycle[++n]=1;
            while (x!=j) flag[x]=1,cycle[n]++,x=a[x];
        }
        ans+=calc();
    }
    n=A+B+C,m++;for (int i=1;i<=n;i++) cycle[i]=1;ans+=calc();
    for (int i=1;i<P;i++) if (i*m%P==1) {ans=ans*i%P;break;}
    cout<<ans;
    return 0;
}

 

转载于:https://www.cnblogs.com/Gloid/p/10164541.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值