题目描述
为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。
在晚宴上,主办方为大家提供了 n−1 种不同的寿司,编号 (1,2,3,dots ,n-1)其中第种寿司的美味度为i+1。(即寿司的美味度为从 2 到 n)
现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小G品尝的寿司种类中存在一种美味度为 x 的寿司,小 W 品尝的寿司中存在一种美味度为 y 的寿司,而 x 与 y 不互质。
现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 p 取模)。注意一个人可以不吃任何寿司。
输入格式
输入文件的第 1 行包含 2 个正整数 n, p 中间用单个空格隔开,表示共有 n 种寿司,最终和谐的方案数要对 p 取模。
输出格式
输出一行包含 1 个整数,表示所求的方案模 p 的结果。
输入输出样例
输入
3 10000
输出
9
输入
4 10000
输出
21
输入
100 100000000
输出
3107203
说明/提示
【数据范围】
(n <= 500)
首先,先明确若不互质也就是他们的质因数集合有交集
一个暴力
若(n <= 30) 那么可以把所有的质数状压,然后dp即可。
正解
但是,(n <= 500) 就会有很多质因数,没法状压,但是发现一个数最多只有一个(>=23)的质因数,那么可以对(<23)的质因数状压,再单独考虑大的质因数。具体的说,可以将所有数按照大质因数(记为Big)排序,对于每一段Big相同的段可以将dp数组赋值给f,g然后再dp,最后再合并回去。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<bitset>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
using namespace std;
#define int long long
inline int read()
{
register int x = 0 , f = 0; register char c = getchar();
while(c < '0' || c > '9') f |= c == '-' , c = getchar();
while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
return f ? -x : x;
}
int n , mod;
int dp[1 << 8 | 1][1 << 8 | 1] , f[1 << 8 | 1][1 << 8 | 1] , g[1 << 8 | 1][1 << 8 | 1];
const int prime[] = {2 , 3 , 5 , 7 , 11 , 13 , 17 , 19};
struct node
{
int S , Big;
void Init(int val)
{
Big = -1; S = 0;
for(int i = 0 ; i < 8 ; ++i)
{
if(val % prime[i] == 0)
{
S |= 1 << i;
while(val % prime[i] == 0) val /= prime[i];
}
}
if(val > 1) Big = val;
return ;
}
}s[520];
inline bool cmp(const node &A , const node &B) { return A.Big < B.Big; }
main()
{
n = read(); mod = read();
for(int i = 2 ; i <= n ; ++i) s[i-1].Init(i); n--;
sort(s + 1 , s + 1 + n , cmp);
dp[0][0] = 1;
for(int i = 1 ; i <= n ; ++i)
{
if(s[i].Big == -1)
{
for(int j = 255 ; ~j ; --j)
for(int k = 255 ; ~k ; --k) if((j & k) == 0)
{
if((k & s[i].S) == 0) (dp[j | s[i].S][k] += dp[j][k]) %= mod;
if((j & s[i].S) == 0) (dp[j][k | s[i].S] += dp[j][k]) %= mod;
}
continue;
}
if(i == 1 || s[i].Big != s[i-1].Big)
memcpy(f , dp , sizeof dp) , memcpy(g , dp , sizeof dp);
for(int j = 255 ; ~j ; --j)
for(int k = 255 ; ~k ; --k) if((j & k) == 0)
{
if((k & s[i].S) == 0) (f[j | s[i].S][k] += f[j][k]) %= mod;
if((j & s[i].S) == 0) (g[j][k | s[i].S] += g[j][k]) %= mod;
}
if(i == n || s[i].Big != s[i+1].Big)
for(int j = 255 ; ~j ; --j)
for(int k = 255 ; ~k ; --k) if((j & k) == 0)
dp[j][k] = ((f[j][k] + g[j][k] - dp[j][k]) % mod + mod) % mod;
}
int ans = 0;
for(int i = 0 ; i <= 255 ; ++i)
for(int j = 0 ; j <= 255 ; ++j) if((i & j) == 0)
(ans += dp[i][j]) %= mod;
cout << ans << '
';
return 0;
}