P2150 [NOI2015]寿司晚宴

题目

P2150 [NOI2015]寿司晚宴

给定\(n-1\)中不同的寿司,第\(i\)种寿司的美味度为\(i+1\),小\(G\)和小\(W\)从中挑选一些来品尝,要求他们选得寿司中美味度必须都互质,问有多少种方案(对\(p\)取模)

\(2\le n \le500,0<p\le10^9\)

思路

  • \(30pts\)

\(n\le 30\)一共有\(10\)个质数,我们可以状压一下两人已经选的集合

显然对于一个寿司,如果它的质因子都不在在小\(G\)的集合里,那么它可以放入小\(W\)的集合里面

\(f[i][s1][s2]\)表示到第\(i\)个寿司,小\(G\)选择的质因子集合是\(s1\),小\(W\)选的是\(s2\)

$\left{\begin{matrix} f[i][s1|p_i][s2]+=f[i-1][s1][s2] & p_i&s2=0 \ f[i][s1][s2|p_i]+=f[i-1][s1][s2] & p_i&s1=0 \end{matrix}\right. $

\(p_i\)为第\(i\)个寿司的质因数集合

上述转移方程竟然可以通过滚动数组来优化(奇怪的知识增加了

复杂度\(O(2^{10}\times n)\),期望得分\(30\)

  • \(40pts\)

没想到有什么只能过\(40pts\)的,如果有只能过\(40pts\)的麻烦告诉蒟蒻一下

  • \(70pts\)

同样不知道,\(qwq\)

  • \(100pts\)

对于\(1\)个数\(n\)它至多有\(1\)\(>\sqrt{n}\)的质因子

根据上面的性质,我们没必要把所有质因子都状压,只需要将质因子进行分类

一类是”小因子“\(<\sqrt{n}\),一类是大“因子”\(> \sqrt{n}\)

\(a[i].first\)表示大因子集合,用\(a[i].second\)表示小因子集合

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

在计算一段相同的大因子的数时,我们可以把\(f[s1][s2]\)拆成\(g1[s1][s2]\)\(g2[s1][s2]\)分别表示这段数都放在集合\(1\)和集合\(2\)里的答案,\(g1,g2\)仍按照\(30pts\)的方程进行转移即可

\(f[s1][s2]=g1[s1][s2]+g2[s1][s2]-f[s1][s2]\)(\(f[s1][s2]\)加了两遍)

最终答案\(ans=\sum\limits_{s1}\sum\limits_{s2}f[s1][s2]\)

注意\(s1=0\)\(s2=0\)的情况

code

/*
@ author:pyyyyyy/guhl37
-----思路------

-----debug-------
忽略了不选的情况了 
*/
#include<bits/stdc++.h>
#include<algorithm>
#define int long long
using namespace std;
const int prime[9]= {2,3,5,7,11,13,17,19};
int g[3][1<<9][1<<9],f[1<<9][1<<9];
int bin[30];
pair<int,int> a[555];
int n,p;
void pre() {
	bin[0]=1;
	for(int i=1; i<=20; ++i) bin[i]=bin[i-1]*2;
	for(int i=2; i<=n; ++i) {
		int x=i;
		for(int j=0; j<=7; ++j) {
			if(x%prime[j]==0) a[i].second|=bin[j];
			while(x%prime[j]==0) x/=prime[j];
		}
		a[i].first=x;
	}
	sort(a+2,a+1+n);
}
signed main() {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cin>>n>>p;
	pre();
	memset(f,0,sizeof(f));
	f[0][0]=1;
	for(int i=2; i<=n; ++i) {
		if(a[i].first==1 || a[i].first!=a[i-1].first || i==2) {
			memcpy(g[1],f,sizeof(g[1]));
			memcpy(g[2],f,sizeof(g[2]));
		}
		for(int s1=255; s1>=0; --s1) {
			for(int s2=255; s2>=0; --s2) {
				if(s1&s2) continue;
				if(!(s2&a[i].second)) g[1][s1|a[i].second][s2]=(g[1][s1|a[i].second][s2]+g[1][s1][s2])%p;
				if(!(s1&a[i].second)) g[2][s1][s2|a[i].second]=(g[2][s1][s2|a[i].second]+g[2][s1][s2])%p;
			}
		}
		if((i==n) || (a[i].first!=a[i+1].first) ||(a[i].first==1)) {
			for(int s1=255; s1>=0; --s1) {
				for(int s2=255; s2>=0; --s2) {
					if(s1&s2) continue;
					f[s1][s2]=(g[1][s1][s2]%p+g[2][s1][s2]%p-f[s1][s2]%p+p)%p;
				}
			}
		}
	}
	int ans=0;
	for(int s1=255; s1>=0; --s1)
		for(int s2=255; s2>=0; --s2)
			ans=(ans+f[s1][s2])%p;
	cout<<(ans%p+p)%p;
	return 0;
}
posted @ 2020-06-18 10:50  pyyyyyy  阅读(270)  评论(2编辑  收藏  举报