[bzoj4197] [NOI2015]寿司晚宴

题目链接

状压\(dp\).

对于\(30\%\)的数据,\(n\leq 30\),质因数的种类最多只有10个,所以可以压缩状态,设状态\(sta\)为第\(i\)位为集合中是否有第\(i\)个质因数。

\(f[i][s1][s2]\)表示当前考虑到第\(i\)个数,第一个集合状态为\(s1\),第二个集合状态为\(s2\)的方案数。

设当前枚举到的数状态为\(k\),显然有转移:\(f[i][s1|k][s2]+=f[i-1][s1][s2]\)\(f[i][s1][s2|k]+=f[i-1][s1][s2]\).

这种刷表转移,根据套路,可以逆循环省掉第一维,时间复杂度\(O(n*2^{20})\).

然后对于所有数据,上面的算法就不行了。但是看一眼数据范围:\(n\leq 500\),还是很小。

可以发现500的范围最多只能有一个大于22的质数,而小于22的质数只有8个。于是先按这个大一点的质数排序,然后一个一个质数的处理。

\(f[i][s1][s2]\)表示当前做到第\(i\)个质数了,前8个质数的状态为\(s1,s2\)的方案数。注意没有比22大的质数的数算一类。

对于当前枚举到的质数\(p\),设\(g[0/1][s1][s2]\)表示状态为\(s1,s2\),当前质数p给第一个/第二个人,且算上质数p及以前的质数的方案数,然后\(g\)的初值可以设为上一个质数的\(f\).这表示不含当前质数的方案数。然后\(g\)的转移可以按照30分的方法转移。

当枚举到含有质数p的最后一个数时,转移完\(g\)时可以利用\(g\)来更新\(f\)

\[f[i][s1][s2]=g[0][s1][s2]+g[1][s1][s2]-f[i-1][s1][s2] \]

因为\(g[0]\)\(g[1]\)都含有不选质数p的情况,所以要减去一个。

然后发现\(f\)的第一维还是可以省掉,时间复杂度\(O(n*2^{16})\)

#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define int long long 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
void print(int x) {
    if(x<0) x=-x,print(x/10);
    if(!x) return ;print(x/10);putchar((x%10)^48);
}void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
int n,f[300][300],g[300][300][2];
int pri[]={0,2,3,5,7,11,13,17,19};
struct node {
    int p,s;
    void init(int x) {
        int num=x;
        for(int i=1;i<=8;i++)
            if(!(num%pri[i])) {
                s|=1<<(i-1);
                while(!(num%pri[i])) num/=pri[i];
            }p=num;
    }
    bool operator < (const node x) const {return p<x.p;}
}a[1000];int mod;
void out(int x) {
    char s[10];
    //itoa(x,s,2);
    printf("%s\n",s);
}
int add(int x,int y) {x+=y;if(x>mod) x-=mod;if(x<0) x+=mod;return x;}
signed main() {
    read(n),read(mod);for(int i=2;i<=n;i++) a[i].init(i);
    //for(int i=2;i<=n;i++) out(a[i].s);
    sort(a+2,a+n+1);f[0][0]=1;
    for(int i=2;i<=n;i++) {
        if(a[i].p!=a[i-1].p||a[i].p==1||i==2)
            for(int s1=0;s1<=255;s1++)
                for(int s2=0;s2<=255;s2++)
                    g[s1][s2][0]=g[s1][s2][1]=f[s1][s2]%mod;
        for(int s1=255;~s1;s1--)    
            for(int s2=255;~s2;s2--) {
                if(!(a[i].s&s1)) g[s1][s2|a[i].s][1]=(g[s1][s2|a[i].s][1]+g[s1][s2][1])%mod;
                if(!(a[i].s&s2)) g[s1|a[i].s][s2][0]=(g[s1|a[i].s][s2][0]+g[s1][s2][0])%mod;
            }
        if(a[i].p!=a[i+1].p||a[i].p==1||i==n) 
            for(int s1=0;s1<=255;s1++)
                for(int s2=0;s2<=255;s2++)
                    f[s1][s2]=((g[s1][s2][0]+g[s1][s2][1]-f[s1][s2])%mod+mod)%mod;
    }
    int ans=0;
    for(int s1=0;s1<=255;s1++)
        for(int s2=0;s2<=255;s2++)
            if(!(s1&s2)) ans=(ans+f[s1][s2])%mod;
    write(ans);
    return 0;
}

posted @ 2018-10-23 21:45  Hyscere  阅读(150)  评论(0编辑  收藏  举报