首页 > 其他 > 详细

BZOJ4197 [Noi2015]寿司晚宴 【状压dp】

时间:2018-06-08 18:55:36      阅读:180      评论:0      收藏:0      [点我收藏+]

题目链接

BZOJ4197

题解

两个人选的数都互质,意味着两个人选择了没有交集的质因子集合
容易想到将两个人所选的质因子集合作为状态\(dp\)

\(n\)以内质数很多,但容易发现\(\sqrt{n} \approx 22.3\),这里边的质数只有\(8\)个,而大于\(\sqrt{n}\)的质因子只会出现一次,可以特殊讨论
我们先将所有数按最大质因子排序,如果最大质因子在那\(8\)个质数里边,就记为\(1\)
我们设\(f[i][s1][s2]\)表示选了前\(i\)个数,两人的质因子集合分别为\(s1\)\(s2\)的方案数
那么显然
\[ans = \sum\limits_{s1 \cap s2 = \emptyset} f[n][s1][s2]\]

对于最大质因子不超过\(sqrt{n}\)的那些数,我们可以枚举加入\(s1\)\(s2\)进行转移
对于同一组最大质因数为\(x\)的数,我们先将\(f\)拷贝到\(g\)中,令\(g[0|1][i][s1][s2]\)表示该最大质因数加入谁中,选了前\(i\)个数,质因子集合为\(s1\)\(s2\)的方案数
求完后令\(f[i][s1][s2] = g[0][i][s1][s2] + g[1][i][s1][s2] - f[i][s1][s2]\),因为原来的\(f\)是不包含这一组数的方案,而两个\(g\)都包含了不包含这一组数的方案数,要减去一个

由于空间问题,\(f\)\(g\)都要通过逆序转移压掉\(i\)那一维

然后就做完了

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 505,maxm = 1 << 8,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
int f[maxm][maxm],g[2][maxm][maxm];
int n,P,a[maxn];
int p[] = {2,3,5,7,11,13,17,19};
struct node{
    int x,p;
}e[maxn];
inline bool operator <(const node& a,const node& b){
    return a.p < b.p;
}
int getp(int x){
    for (int i = 0; i < 8; i++)
        if (x % p[i] == 0) while (x % p[i] == 0) x /= p[i];
    return x;
}
void init(){
    for (int i = 2; i <= n; i++)
        for (int k = 0; k < 8; k++)
            if (i % p[k] == 0)
                a[i] |= (1 << k);
}
int main(){
    n = read(); P = read(); init();
    for (int i = 2; i <= n; i++) e[i].x = i,e[i].p = getp(i);
    sort(e + 2,e + 1 + n);
    f[0][0] = 1;
    for (int i = 2; i <= n; i++){
        int x = e[i].x,s = a[x];
        if (e[i].p == 1){
            for (int j = maxm - 1; ~j; j--){
                for (int k = maxm - 1; ~k; k--){
                    int t = f[j][k];
                    f[j][k | s] = (f[j][k | s] + t) % P;
                    f[j | s][k] = (f[j | s][k] + t) % P;
                }
            }
        }
        else {
            for (int j = 0; j < maxm; j++)
                for (int k = 0; k < maxm; k++)
                    g[0][j][k] = g[1][j][k] = f[j][k];
            int nxt = i;
            while (nxt < n && e[nxt + 1].p == e[i].p) nxt++;
            for (int l = 0; l <= nxt - i; l++){
                x = e[l + i].x,s = a[x];
                for (int j = maxm - 1; ~j; j--){
                    for (int k = maxm - 1; ~k; k--){
                        g[0][j | s][k] = (g[0][j | s][k] + g[0][j][k]) % P;
                        g[1][j][k | s] = (g[1][j][k | s] + g[1][j][k]) % P;
                    }
                }
            }
            i = nxt;
            for (int j = 0; j < maxm; j++)
                for (int k = 0; k < maxm; k++)
                    f[j][k] = (((g[1][j][k] + g[0][j][k]) % P - f[j][k]) % P + P) % P;
        }
    }
    int ans = 0;
    for (int j = 0; j < maxm; j++)
        for (int k = 0; k < maxm; k++)
            if (!(j & k)) ans = (ans + f[j][k]) % P;
    printf("%d\n",ans);
    return 0;
}

BZOJ4197 [Noi2015]寿司晚宴 【状压dp】

原文:https://www.cnblogs.com/Mychael/p/9157134.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!