• [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;
    }
    
    
  • 相关阅读:
    windows中dos命令指南
    HDU 2084 数塔 (dp)
    HDU 1176 免费馅饼 (dp)
    HDU 1004 Let the Balloon Rise (map)
    变态杀人狂 (数学)
    HDU 2717 Catch That Cow (深搜)
    HDU 1234 开门人和关门人 (模拟)
    HDU 1070 Milk (模拟)
    HDU 1175 连连看 (深搜+剪枝)
    HDU 1159 Common Subsequence (dp)
  • 原文地址:https://www.cnblogs.com/hbyer/p/9839604.html
Copyright © 2020-2023  润新知