P2150 [NOI2015] 寿司晚宴

传送门 : 寿司晚宴

【Description】:

给定 \(n - 1\) 个数 \(a_i\) , \(a_i\) 满足 \(a_i = i + 1\) , 将 \(n - 1\) 个数分成两堆,两堆必须满足互质,求解方案数。

  • 对于 \(\text{30%}\)数据 满足 \(n \leq 30\)
  • 对于 \(\text{100%}\) 数据 满足 \(n \leq 500\)

【Solution】:

Part 1

对于 \(30\text{%}\) 的数据 , 我们显然能够想到,我们状压 \(G\)\(W\) 选择的质数集。

方案数合法的充要条件 :

\(G\) 所选的集合 \(P\)\(W\) 选择 \(W\) 的集合满足 : \(G\in S , W \in T , S \cap T = \phi\)
\(S , T\) 是两个大质数集。

我们发现 \(n \leq 30\) 的时候,最多只有 \(10\) 个 质数。

为了表示整个局面和下一步的转移,我们设 \(f_{i,G,W}\) 为 选完 \(i\) 这个寿司,\(G\) 选择的质数集, \(W\) 选择的质数集 为 \(G , W\) ,显然我们对于状态转移,就是这个寿司是选择,就有状态转移 :

\[{f_{i , G | P_i , W} += f_{i - 1 , G , W} }, [P_i \text{&} W = 0] \]

\[f_{i , G , W|P_i} += f_{i - 1 , G , W} , [P_i \text{&}W = 0] \]

\(P_i\) 则是代表第 \(i\) 个寿司的美味度的质因数集。

\[Then \ \ we \ \ can \ \ get \ \ 30 \ \ points . \]

Part 2 , 3

\(n \leq 200\) 的时候,想不出来和 \(n\leq 500\) 有什么不一样的做法。

Part 4

\(n \leq 500\) 时 , 显然状压 \(500\) 以内的质因子集是不大现实,\(500\) 以内的质数个数是 \(95\) 个来着,状压个锤子 。

有一个结论可能会帮你完成该题

对于一个数 \(n\) 最多有一个 > \(\sqrt n\) 的质因子。

那么我们就能够直接取出这一个质因子即可。 设为 \(big\) ,其他我们设为 \(small\)\(\sqrt 500\) 大概取 \(22\) ,所以 我们求解 \(small\) 的时候,只需要将 \(22\) 以内的质数搞一下分解就好。

所有 \(big\) 相同的数显然只能放在一个集合中,所以按照 \(big\) 的大小排序,这样就可以把所有 \(big\) 相同的数放在一起算 。

在计算 \(big\) 相同的时候,我们发现,就和 \(30 \ \ opts\) 是一样的,我们设 \(f_1[P][W] ,f_2[P][W]\) 去转移 \(30 \ \ opts\) 的状态转移 。

小小的容斥原理 \(f_{P,W}\) 加了两遍

\(f_{P,W} = f_1[P][W] + f_2[P][W] - f[P][W]\) , 。

最后取答案 \(ans = \sum_{P}\sum_{W} f_{P,W}\)

【Code】

/*
Author : Zmonarch
知识点 :代码实现看的第一篇题解。
*/
#include <bits/stdc++.h>
#define int long long
#define qwq register
#define qaq inline
#define inf 2147483647
using namespace std ;
const int kmaxn = 550 ;
const int kmod = 1e9 + 7 ;
const int eps = 1e-6 ;
const int num[] = {0 , 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19} ; 
inline int read() {
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) {if(ch == '-') f = - 1 ; ch = getchar() ;}
	while( isdigit(ch)) {x = x * 10 + ch - '0' ; ch = getchar() ;}
	return x * f ;
}
int n , mod , ans ; 
int f[kmaxn][kmaxn] , f1[kmaxn][kmaxn] , f2[kmaxn][kmaxn] ; 
struct node 
{
	int S = 0 , big , val ; //质数集合 , 大质数
	void init()  
	{
		int tmp = val ; big = 0 ;
		for(int i = 1 ; i <= 8 ; i++) //分解质因数 	
		{
			if(tmp % num[i]) continue ; 
			S |= (1 << (i - 1)) ;  
			while((tmp % num[i]) == 0) tmp /= num[i] ;  
		}
		if(tmp != 1) big = tmp ; 
	}
}a[kmaxn] ; 
inline bool cmp(node a , node b) {return a.big < b.big ;} 
signed main()
{
	n = read() , mod = read() ; 
	for(qwq int i = 2 ; i <= n ; i++) a[i - 1].val = i , a[i - 1].init() ; 
	std::sort(a + 1 , a + n , cmp) ;f[0][0] = 1 ; 
	for(qwq int i = 1 ; i <= n - 1 ; i++)
	{
		if((i == 1) || (a[i].big != a[i - 1].big) || (!a[i].big)) 
		{
			memcpy(f1 , f , sizeof(f1)) ; 
			memcpy(f2 , f , sizeof(f2)) ; 
		}
		for(int j=255; j>=0; j--) 
		  for(int k=255; k>=0; k--) 
		  {
		  	if(j & k) continue ; 
		  	if(!(a[i].S & j)) f2[j][k | a[i].S] = (f2[j][k | a[i].S] + f2[j][k]) % mod ; 
		  	if(!(a[i].S & k)) f1[j | a[i].S][k] = (f1[j | a[i].S][k] + f1[j][k]) % mod;
		  }
		if((i == n - 1) || (a[i].big != a[i + 1].big) || (!a[i].big))
		{
			for(int j=0; j<=255; j++) 
			 for(int k=0; k<=255; k++) 
			 {
			  	if(j & k) continue ; 
			  	f[j][k] = ( (f1[j][k] + f2[j][k] ) % mod - f[j][k] + mod) % mod ;
			 }
		} 
	}
	ans = 0 ; 
	for(int j=0; j<=255; j++) 
	 for(int k=0; k<=255; k++) 
	  if(!(j & k)) ans = (ans + f[j][k]) % mod ;
	printf("%lld\n" , ans) ; 
	return 0 ;
}
posted @ 2021-06-07 14:37  SkyFairy  阅读(98)  评论(1编辑  收藏  举报