bzoj 4197: [Noi2015]寿司晚宴 状压dp

来源:互联网 发布:网管软件 免费 编辑:程序博客网 时间:2024/04/29 07:08

       这题其实N出到800都没问题吧。。

       显然,如果两个数有一个质因数相同则不能在两边;更进一步,按照最大质因数,可以把1~N的所有的数分组,那么统一组的不能在两边。显然次小质因数必然<=N^0.5,只有2,3,5,7,11,13,17,19这8个数,那么可以把这8个质因数是否存在压成一个状态,那么f[i][j][k]就可以表示第一个人的集合为i,第二个人的集合为j,当前组是分给第一个人(k=1),第二个人(k=2),或者当前组没有选(k=0),就可以O(1)转移了。

       实际上这样是O(N*4^8),但是注意到最终只有i&j=0的才有用。因此有用的状态只有3^8种,因此就是O(N*3^8)。

AC代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define N 65536using namespace std;const int prm[8]={2,3,5,7,11,13,17,19};int n,mod,cnt,bin[25],f[N][3],g[3],p[N];struct node{ int x,y; }a[505];bool cmp(const node &u,const node &v){ return u.x<v.x; }bool check(int x){for (; x; x>>=2) if ((x&3)==3) return 0;return 1;}int main(){scanf("%d%d",&n,&mod); int i,j,k,l;bin[0]=1; for (i=1; i<15; i++) bin[i]=bin[i-1]<<1;for (i=2; i<=n; i++){for (j=0,k=i; j<8 && k>1; j++) if (!(k%prm[j])){a[i].y|=bin[j<<1];while (!(k%prm[j])) k/=prm[j];}a[i].x=k;}sort(a+2,a+n+1,cmp);for (i=0; i<N; i++) if (check(i)) p[++cnt]=i;f[0][0]=1;for (i=2; i<=n; i++){if (a[i].x!=a[i-1].x || a[i].x==1)for (j=1; j<=cnt; j++){k=p[j];f[k][0]=(f[k][0]+f[k][1])%mod; f[k][0]=(f[k][0]+f[k][2])%mod;f[k][1]=f[k][2]=0;}for (j=cnt; j; j--){k=p[j]; memcpy(g,f[k],sizeof(f[k]));for (l=0; l<3; l++) if (g[l]){if (l!=2 && !(k&(a[i].y<<1))) f[k|a[i].y][1]=(f[k|a[i].y][1]+g[l])%mod;if (l!=1 && !(k&a[i].y)) f[k|(a[i].y<<1)][2]=(f[k|(a[i].y<<1)][2]+g[l])%mod;}}}long long ans=0;for (i=1; i<=cnt; i++)for (j=0; j<3; j++) ans+=f[p[i]][j];printf("%lld\n",ans%mod);return 0;}

by lych

2016.5.3

0 0