题面

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 \times 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\), 我们有如下转移:

\[\begin{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\n", ans);
}

BZOJ 4197 NOI 2015 寿司晚宴的更多相关文章

  1. BZOJ 4197 NOI 2015 寿司晚宴 状压DP

    4197: [Noi2015]寿司晚宴 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 694  Solved: 440[Submit][Status] ...

  2. [NOI 2015]寿司晚宴

    Description 题库链接 给定 \(2\sim n\) 一共 \(n-1\) 个数字,第一个人选择一些数字,第二个人选择一些数字,要求第一个人选的任意一个数字和第二个人选择的任意一个数字都互质 ...

  3. NOI 2015 寿司晚宴 (状压DP+分组背包)

    题目大意:两个人从2~n中随意取几个数(不取也算作一种方案),被一个人取过的数不能被另一个人再取.两个人合法的取法是,其中一个人取的任何数必须与另一个人取的每一个数都互质,求所有合法的方案数 (数据范 ...

  4. [LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机

    [LOJ 2134][UOJ 132][BZOJ 4200][NOI 2015]小园丁与老司机 题意 给定平面上的 \(n\) 个整点 \((x_i,y_i)\), 一共有两个问题. 第一个问题是从原 ...

  5. [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会

    [LOJ 2133][UOJ 131][BZOJ 4199][NOI 2015]品酒大会 题意 给定一个长度为 \(n\) 的字符串 \(s\), 对于所有 \(r\in[1,n]\) 求出 \(s\ ...

  6. bzoj 4199 && NOI 2015 品酒大会

    一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 ...

  7. [bzoj 4196][NOI 2015]软件包管理器

    大概算是一道模板题吧? 就是细节有点多 罗列一下: 如果习惯从1开始搞树的编号的话,处理输入进来的那个依赖关系在加边的时候两个都要+1,体现在代码就是i要从2枚举到n,然后输入进来的那个数要+1 这道 ...

  8. bzoj 4198 [ Noi 2015 ] 荷马史诗 —— 哈夫曼编码(k叉哈夫曼树)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4198 第一次写哈夫曼树!看了很多博客. 哈夫曼树 & 哈夫曼编码:https://w ...

  9. BZOJ 4197: [Noi2015]寿司晚宴( dp )

    N^0.5以内的质数只有8个, dp(i, j, k)表示用了前i个大质数(>N^0.5), 2人选的质数(<=N^0.5)集合分别为j, k时的方案数. 转移时考虑当前的大质数p是给哪个 ...

随机推荐

  1. 常用的一些api

    发送手机短信 // 发送短信给安全号码 SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(phon ...

  2. Dataflow编程模型和spark streaming结合

    Dataflow编程模型和spark streaming结合 主要介绍一下Dataflow编程模型的基本思想,后面再简单比较一下Spark  streaming的编程模型 == 是什么 == 为用户提 ...

  3. Java并发之(1):volatile关键字(TIJ21-21.3.3 21.3.4)

    Java并发Java服务器端编程的一项必备技能. ** 1 简介    volatile是java中的一个保留关键字,它在英语中的含义是易变的,不稳定的.volatile像final.static等其 ...

  4. 50、转自知乎上android开发相见恨晚的接口

      原文链接:http://www.zhihu.com/question/33636939     程序员软件开发Android 开发JavaAndroid修改 Android开发中,有哪些让你觉得相 ...

  5. Pass Data Between ASP.NET Pages

    There is the data to send in current page <asp:TextBox ID="DataToSendTextBox" runat=&qu ...

  6. CSU-1985 驱R符

    CSU-1985 驱R符 Description 阴阳师中有三中稀有度的式神R,SR,SSR,其中R的稀有度最低,每次抽符,会随机得到一种式神,然而子浩君对R式神已经深恶痛绝. 某天,子浩君突然发现, ...

  7. Html语言的标签讲解

    一.head头部中的内容: 1.<meta charset="UTF-8"> <--!告诉浏览器什么编码--> 2.<meta http-equiv= ...

  8. python 打印9*9乘法表

    # -*- coding: utf8 -*- # Author:wxq 1. for i in range(1,10): for j in range(1,i+1): print "%d*% ...

  9. aspx页面直接访问后台方法

    在方法上面机上[WebMethod]就可以直接请求该方法了.

  10. vim 查找命令

    /要查找的内容      自光标起始位置向下查找 ?要查找的内容   自光标起始位置向上查找