bzoj4197 [Noi2015]寿司晚宴

来源:互联网 发布:物流企业软件供应商 编辑:程序博客网 时间:2024/04/20 10:19

【题意】

给出2~n共(n-1)个数,每个数可以放入A集合、放入B集合或不放,满足A集合任意一个数与B集合任意一个数均互质,求方案数。

【数据范围】

n<=500

【思路】

好题

核心思想:将质数按sqrt(n)分成两类!

[1]<=sqrt(n)的质数<=8个

[2]>sqrt(n)的质数有很多,但是2~n中的每个数分解质因数后这类质因子最多有1个

枚举2~n中每个数,若i中存在[2]类质因子x(最多1个),把i归为x那一组

将A、B集合的数分解质因数,在一种合法方案中,A、B集合的质因子交集为空

对[1]质数状压dp,f[s1][s2]=A集合中质因子存在状态为s1,B集合中质因子存在状态为s2,合法方案数

初始f[0][0]=1

对于没有分组的2~n中的数,直接O(1)转移

枚举每一组,则这一组的所有数要么均不能加入A集合,要么均不能加入B集合,分这两种情况对这一组整体DP,将两种情况答案相加,注意所有数均不加入的情况被统计了2次,需要减掉1次(即减掉f初值)

由于A、B集合元素交集为空,故也可以使用3^8状压

【时间复杂度】

O(n*4^8)

#include<cstdio>#include<cstring>#include<algorithm>#define N 510using namespace std; int n, mod, flag[N], p[N], l, sum, L[N], w[N], map[N][N], f[260][260], g[260][260], l1,a[10], bin[20], now, ans, f1[260][260], f2[260][260], g1[260][260], g2[260][260]; void prev(){    memset(flag, 0, sizeof(flag)); l=0;    for(int i=2; i<=n; i++){        if(!flag[i])p[++l]=i;        for(int j=1; j<=l&&i*p[j]<=n; j++){            flag[i*p[j]]=1;            if(i%p[j]==0)break;        }    }} int main(){    scanf("%d%d", &n, &mod);    bin[0]=1; for(int i=1; i<=10; i++)bin[i]=bin[i-1]<<1;    prev();    sum=0; for(int i=1; i<=l; i++)if(p[i]*p[i]<=500)sum++;    for(int i=1; i<=l-sum; i++){w[i]=p[i+sum]; L[i]=0;} L[0]=0;    memset(flag, 0, sizeof(flag));    for(int i=2; i<=n; i++)        for(int j=1; j<=l-sum; j++)if(i%w[j]==0){map[j][++L[j]]=i; flag[i]=j;}    for(int i=2; i<=n; i++)if(!flag[i])map[0][++L[0]]=i;    f[0][0]=1;    for(int i=1; i<=L[0]; i++){        now=0;        for(int j=1; j<=sum; j++)if(map[0][i]%p[j]==0)now+=bin[j-1];        memset(g, 0, sizeof(g));        for(int j=0; j<=bin[sum]-1; j++)            for(int k=0; k<=bin[sum]-1; k++)if(f[j][k]){                g[j][k]=(g[j][k]+f[j][k])%mod;                g[j|now][k]=(g[j|now][k]+f[j][k])%mod;                g[j][k|now]=(g[j][k|now]+f[j][k])%mod;            }        for(int j=0; j<=bin[sum]-1; j++)            for(int k=0; k<=bin[sum]-1; k++)if(j&k)g[j][k]=0;        memcpy(f, g, sizeof(f));    }    for(int i=1; i<=l-sum; i++){        memcpy(f1, f, sizeof(f1)); memcpy(f2, f, sizeof(f2));        for(int j=1; j<=L[i]; j++){            memset(g1, 0, sizeof(g1)); memset(g2, 0, sizeof(g2));            now=0;            for(int k=1; k<=sum; k++)if(map[i][j]%p[k]==0)now+=bin[k-1];            for(int k=0; k<=bin[sum]-1; k++)                for(int m=0; m<=bin[sum]-1; m++){                    g1[k][m]=(g1[k][m]+f1[k][m])%mod;                    g1[k|now][m]=(g1[k|now][m]+f1[k][m])%mod;                    g2[k][m]=(g2[k][m]+f2[k][m])%mod;                    g2[k][m|now]=(g2[k][m|now]+f2[k][m])%mod;                }            for(int k=0; k<=bin[sum]-1; k++)                for(int m=0; m<=bin[sum]-1; m++)if(k&m)g1[k][m]=g2[k][m]=0;            memcpy(f1, g1, sizeof(f1)); memcpy(f2, g2, sizeof(f2));        }        memset(g, 0, sizeof(g));        for(int j=0; j<=bin[sum]-1; j++)            for(int k=0; k<=bin[sum]-1; k++){                g[j][k]=(f1[j][k]+f2[j][k]-f[j][k])%mod;                if(g[j][k]<0)g[j][k]+=mod;            }        for(int j=0; j<=bin[sum]-1; j++)            for(int k=0; k<=bin[sum]-1; k++)if(j&k)g[j][k]=0;        memcpy(f, g, sizeof(f));    }    ans=0;    for(int i=0; i<=bin[sum]-1; i++)        for(int j=0; j<=bin[sum]-1; j++)ans=(ans+f[i][j])%mod;    printf("%d", ans);}


0 0
原创粉丝点击