BZOJ4347 POI2016Nim z utrudnieniem(博弈+动态规划)

  由nim游戏的结论,显然等价于去掉一些数使剩下的数异或和为0。

  暴力的dp比较显然,设f[i][j][k]为前i堆移走j堆(模意义下)后异或和为k的方案数。注意到总石子数量不超过1e7,按ai从小到大排序,这样k的枚举范围就不会超过2ai,于是复杂度O(md)。

  注意空间卡的非常紧,连滚动都开不下,改为留下的有j堆(模意义下)可能比较方便,存一下j=d-1时的数组,对j=1~d-1倒序转移完后再特判j=0即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
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;
}
#define N 500010
#define P 1000000007
int n,m,a[N],u[N<<1],f[10][1<<20],tmp[1<<20];
inline void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4347.in","r",stdin);
    freopen("bzoj4347.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read();
    for (int i=1;i<=n;i++) a[i]=read();
    sort(a+1,a+n+1);
    int t=1;
    for (int i=1;i<=1000000;i++)
    {
        if (t<i) t=t<<1|1;
        u[i]=t;
    }
    f[0][0]=1;
    for (int i=1;i<=n;i++)
    {
        memcpy(tmp,f[m-1],u[a[i]]+1<<2);
        for (int j=m-1;j>=1;j--)
            for (int k=0;k<=u[a[i]];k++)
            inc(f[j][k],f[j-1][k^a[i]]);
        for (int k=0;k<=u[a[i]];k++)
        inc(f[0][k],tmp[k^a[i]]);
    }
    cout<<(f[n%m][0]-(n%m==0)+P)%P;
    return 0;
}

 

posted @ 2018-10-27 17:46  Gloid  阅读(185)  评论(0编辑  收藏  举报