[bzoj4197] [NOI2015]寿司晚宴

题目链接

状压(dp).

对于(30\%)的数据,(nleq 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})).

然后对于所有数据,上面的算法就不行了。但是看一眼数据范围:(nleq 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('
');}
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
",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;
}

原文地址:https://www.cnblogs.com/hbyer/p/9839604.html