zoukankan      html  css  js  c++  java
  • BZOJ 4197 NOI 2015 寿司晚宴

    题面

    Description

    为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。
    在晚宴上,主办方为大家提供了 n−1 种不同的寿司,编号 1,2,3,…,n−1,其中第 i 种寿司的美味度为 i+1 (即寿司的美味度为从 2 到 n)。
    现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 x 的寿司,小 W 品尝的寿司中存在一种美味度为 y 的寿司,而 x 与 y 不互质。
    现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 p 取模)。注意一个人可以不吃任何寿司。

    Input

    输入文件的第 1 行包含 2 个正整数 n,p,中间用单个空格隔开,表示共有 n 种寿司,最终和谐的方案数要对 p 取模。

    Output

    输出一行包含 1 个整数,表示所求的方案模 p 的结果。

    Samples

    input:

    3 10000
    

    Sample Output

    9
    

    HINT

    (2≤n≤500)
    (0<p≤1000000000)

    Solution

    考虑(n)比较小的情况: 我们用f[i][j][k]表示到第(i)个数, 其中两个取的质因子情况分别为(j)(k)的方案数, 01背包去掉第一维直接递推即可. 时间复杂度: (O(n imes 2^{2n}))
    我们注意到, 对于一个数(n), 其大于(sqrt{n})的质因数最多只有一个, 因此我们对小于(500)的质数进行分组, (le sqrt{500})的分为一组, (> sqrt{500})的分为另一组, 则每个数至多只会包含一个第二组的质因数. 我们首先对第一组的素数按照前面的方法进行状压DP, 再考虑第二组.
    我们对第二组中每个素数记录一个集合(S), 表示包含有这一个质因子的数的集合. 对于同一个(S)中的数, 要么两个人都不选, 要么只由一个人选. 因此对于一个集合, 我们用g[i = 0 / 1][j][k]来表示由(i)来选这个集合中的数, 且两个人所选小于等于(sqrt{n})的约数情况分别为(j), (k)的方案数. 开始时(g[0 / 1][j][k] = f[j][k]). 对于同一个集合中的一个元素(a), 我们有如下转移:

    [egin{cases} g[0][j cup a][k] = g[0][j cup a][k] + g[0][j][k] \ g[1][j][k cup a] = g[1][j][k cup a] + g[1][j][k] end{cases} ]

    当处理完一个集合中的数后, 我们更新(f)数组:

    [f[j][k] = g[0][j][k] + g[1][j][k] - f[j][k] ]

    其中(f[j][k])表示更新前的(f)值. 将其减去的原因是(g[0])(g[1])都计算了一次整个集合都不取的情况, 因此要减掉一次.
    注意到有一些数不存在大于(sqrt{500})的因数, 我们把它们和小于等于(sqrt{500})的放在一起计算即可.

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #define min std::min
    #define vector std::vector
    
    const int N = 500;
    int n, p;
    int prm[N], isNotPrime[N + 1], cnt;
    vector<int> bck[N];
    inline void getPrime()
    {
        memset(isNotPrime, 0, sizeof(isNotPrime));
        cnt = 0;
        for(int i = 2; i <= 500; ++ i)
        {
            if(! isNotPrime[i]) prm[cnt ++] = i;
            for(int j = 0; j < cnt && i * prm[j] <= 500; ++ j)
            {
                isNotPrime[i * prm[j]] = 1;
                if(i % prm[j] == 0) break;
            }
        }
    }
    int main()
    {
        scanf("%d%d", &n, &p);
        getPrime();
        int bnd = 8;
        int f[1 << bnd][1 << bnd]; memset(f, 0, sizeof(f)); f[0][0] = 1;
        for(int i = 2; i <= min(n, 22); ++ i)
        {
            int stt = 0; for(int j = 0; j < bnd; ++ j) if(i % prm[j] == 0) stt |= 1 << j;
            for(int j = (1 << bnd) - 1; ~ j; -- j) for(int k = (1 << bnd) - 1; ~ k; -- k)
                f[j | stt][k] = (f[j | stt][k] + f[j][k]) % p, f[j][k | stt] = (f[j][k | stt] + f[j][k]) % p;
        }
        for(int i = 23; i <= n; ++ i)
        {
            int flg = 0;
            for(int j = cnt - 1; j >= 8; -- j) if(i % prm[j] == 0)
            {
                flg = 1;
                bck[j].push_back(i);
            }
            if(! flg)
            {
                int stt = 0; for(int j = 0; j < bnd; ++ j) if(i % prm[j] == 0) stt |= 1 << j;
                for(int j = (1 << bnd) - 1; ~ j; -- j) for(int k = (1 << bnd) - 1; ~ k; -- k)
                    f[j | stt][k] = (f[j | stt][k] + f[j][k]) % p, f[j][k | stt] = (f[j][k | stt] + f[j][k]) % p;
            }
        }
        int g[2][1 << bnd][1 << bnd];
        for(int i = 8; i < cnt; ++ i) if(! bck[i].empty())
        {
            for(int j = 0; j < 2; ++ j) for(int k = 0; k < 1 << bnd; ++ k) for(int l = 0; l < 1 << bnd; ++ l) g[j][k][l] = f[k][l];
            for(auto cur : bck[i])
            {
                int stt = 0; for(int j = 0; j < bnd; ++ j) if(cur % prm[j] == 0) stt |= 1 << j;
                for(int j = (1 << bnd) - 1; ~ j; -- j) for(int k = (1 << bnd) - 1; ~ k; -- k)
                    g[0][j | stt][k] = (g[0][j | stt][k] + g[0][j][k]) % p, g[1][j][k | stt] = (g[1][j][k | stt] + g[1][j][k]) % p;
            }
            for(int j = 0; j < 1 << bnd; ++ j) for(int k = 0; k < 1 << bnd; ++ k) f[j][k] = ((g[0][j][k] + g[1][j][k]) % p - f[j][k] + p) % p;
        }
        int ans = 0;
        for(int i = 0; i < 1 << bnd; ++ i) for(int j = 0; j < 1 << bnd; ++ j) if((i & j) == 0) ans = (ans + f[i][j]) % p;
        printf("%d
    ", ans);
    }
    
    
  • 相关阅读:
    线程的实现方式
    实现一个拷贝文件的工具类,要使用字符流还是字节流
    String&&StringBuilder&&StringBuffer
    面向对象的特征
    索引的选择
    TCP之Nagle算法&&延迟ACK
    通用套接字选项和TCP套接字选项
    TCP之非阻塞connect和accept
    TCP之种种连接异常
    TCP之listen&backlog
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7445933.html
Copyright © 2011-2022 走看看