1. 程式人生 > >BZOJ4347 POI2016Nim z utrudnieniem(博弈+動態規劃)

BZOJ4347 POI2016Nim z utrudnieniem(博弈+動態規劃)

define lin sort mes char urn ont ring 數組

  由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; }

BZOJ4347 POI2016Nim z utrudnieniem(博弈+動態規劃)