P2150 [NOI2015]寿司晚宴

题目描述

为了庆祝 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 << '\n';
	return 0;
}
posted @ 2020-04-24 20:05  沙野博士  阅读(168)  评论(0)    收藏  举报