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;
}