ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【题解】P2150 [NOI2015] 寿司晚宴

2022-07-01 20:34:30  阅读:155  来源: 互联网

标签:f1 f2 P2150 题解 S1 NOI2015 num S2 dp


题意

P2150 [NOI2015] 寿司晚宴

对于 \(2\) 到 \(n\) 共 \(n - 1\) 个自然数,考虑从中选取一些数并将其分成两部分,使得从两部分中各取任意一个数 \(x, y\) 都满足 \(x, y\) 互质。求选数并分数的方案总数,结果对给定常数 \(p\) 取模。

对于 \(30\%\) 的数据,\(2 \leq n \leq 30\)

对于所有数据,\(2 \leq n \leq 500, 0 < p \leq 10^9\)

思路

状压 dp

先考虑 \(30\%\) 的部分分。容易发现判断是否互质只与两部分中每个数含有的质因子有关,考虑到 \(30\) 以内的质数只有 \(10\) 个,故可以设 \(dp[S1][S2]\) 表示部分一中所有数的质因子并集为 \(S1\),\(S2\) 同理的方案总数。不妨预处理出 \([2, n]\) 中每个数的质因子的并集,记为 \(k\),容易得到状态转移方程(刷表法):

当 \(S1 \cap S2 = \empty\) 且 \(k \cap S2 = \empty\) 时,\(dp[S1 \mid k][S2] += dp[S1][S2]\)

当 \(S1 \cap S2 = \empty\) 且 \(k \cap S1 = \empty\) 时,\(dp[S1][S2 \mid k] += dp[S1][S2]\)

时间复杂度是 \(O(2^{10} n)\)

容易发现时间复杂度的瓶颈在于状压的质数个数,\(500\) 以内的质数共有 \(95\) 个,因此需要优化。

易得:对于自然数 \(n\),其大于 \(\sqrt{n}\) 的质因子至多只有 \(1\) 个。

因此可得对于 \(500\) 以内的自然数,其大于 \(\sqrt{500}\)(约为 \(22\))的质因子只有 \(1\) 个。

所以可以考虑把大于 \(22\) 的质因子单独提出来判断。对于 \([2, n]\) 内每一个可能被选中且大于 \(22\) 的质因子,讨论其对答案的贡献。因为大于 \(22\) 的质因子额外讨论,状态只需要记录 \(22\) 以内的质因子是否被选中,即至多有 \(2^8\) 种状态(\(22\) 以内的质数共 \(8\) 个)

考虑记录下 \([2, n]\) 中每一个数的质因子子集 \(k\) 和其大于 \(22\) 的唯一质因子 \(p\)。直接上手不好维护,考虑将所有数按照 \(p\) 分类。对于 \(p\) 相同的数,将其划为一段,分类讨论:

  1. \(p\) 在部分一内

  2. \(p\) 在部分二内

  3. \(p\) 既不在部分一内,也不在部分二内

由于状态只压到 \(22\) 以内的质数,\(p\) 被分在不同部分的情况需要分类讨论。设 \(f1[S1][S2]\) 表示部分一中所有数 \(22\) 以内的质因子并集为 \(S1\),部分二的为 \(S2\) 且所有被 \(p\) 整除的数都不在部分二内的情况。\(f2[S1][S2]\) 同理。对于 \(p \mid x\),容易得到状态转移方程:

当 \(S1 \cap S2 = \empty\) 且 \(k \cap S2 = \empty\) 时,\(f1[S1 \mid k][S2] += f1[S1][S2]\)

当 \(S1 \cap S2 = \empty\) 且 \(k \cap S1 = \empty\) 时,\(f2[S1][S2 \mid k] += f1[S1][S2]\)

每一段开始时,将 \(dp\) 赋值到 \(f1, f2\) 作为初值。当每一段结束以后,考虑重新用 \(f1, f2\) 合并成 \(dp\)。容易发现 \(f1, f2\) 中重复的部分为两部分均没有 \(p\) 的情况,其并集为 部分一包含 \(p\) 并 部分二包含 \(p\) 并 均不包含 \(p\),所以得到:

\(dp[S1][S2] = f1[S1][S2] + f2[S1][S2] - dp[S1][S2]\)

最终的答案为所有合法状态的答案之和。

时间复杂度 \(O(2^{16} n)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

const int maxn = 505;
const int maxs = (1 << 8);

struct item
{
    int fac, sp;
    bool operator < (const item& rhs) const { return (sp < rhs.sp); }
} num[maxn];

int n, p;
int prime[8] = {2, 3, 5, 7, 11, 13, 17, 19};
int dp[maxs][maxs], f1[maxs][maxs], f2[maxs][maxs];

void init()
{
    for (int i = 2; i <= n; i++)
    {
        int cur = i;
        num[i - 1].sp = -1;
        for (int j = 0; j < 8; j++)
        {
            if (!(cur % prime[j]))
            {
                num[i - 1].fac |= (1 << j);
                while (!(cur % prime[j])) cur /= prime[j];
            }
        }
        if (cur > 1) num[i - 1].sp = cur;
    }
    sort(num + 1, num + n);
}

int main()
{
    int ans = 0;
    scanf("%d%d", &n, &p);
    init();
    dp[0][0] = 1;
    for (int i = 1; i <= n - 1; i++)
    {
        if ((num[i].sp != num[i - 1].sp) || (num[i].sp == -1))
        {
            memcpy(f1, dp, sizeof(f1));
            memcpy(f2, dp, sizeof(f2));
        }
        for (int j = maxs - 1; j >= 0; j--)
        {
            for (int k = maxs - 1; k >= 0; k--)
            {
                if (j & k) continue;
                if (!(num[i].fac & k)) f1[j | num[i].fac][k] = (ll)(f1[j | num[i].fac][k] + f1[j][k]) % p;
                if (!(num[i].fac & j)) f2[j][k | num[i].fac] = (ll)(f2[j][k | num[i].fac] + f2[j][k]) % p;
            }
        }
        if ((num[i].sp != num[i + 1].sp) || (num[i].sp == -1))
        {
            for (int j = 0; j < maxs; j++)
                for (int k = 0; k < maxs; k++)
                    if (!(j & k)) dp[j][k] = (ll)((f1[j][k] + f2[j][k]) % p - dp[j][k] % p + p) % p;
        }
    }
    for (int i = 0; i < maxs; i++)
        for (int j = 0; j < maxs; j++)
            if (!(i & j)) ans = (ll)(ans + dp[i][j]) % p;
    printf("%d\n", ans);
    return 0;
}

标签:f1,f2,P2150,题解,S1,NOI2015,num,S2,dp
来源: https://www.cnblogs.com/lingspace/p/p2150.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有