[NOI2015][洛谷P2150]寿司晚宴
题目描述
为了庆祝NOI的成功开幕,主办方为大家准备了一场寿司晚宴。小G和小W作为参加NOI的选手,也被邀请参加了寿司晚宴。
在晚宴上,主办方为大家提供了n−1种不同的寿司,编号1,2,3,⋯,n-1,其中第种寿司的美味度为i+1(即寿司的美味度为从2到n)。
现在小G和小W希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小G品尝的寿司种类中存在一种美味度为x的寿司,小W品尝的寿司中存在一种美味度为y的寿司,而x与y不互质。
现在小G和小W希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数p取模)。注意一个人可以不吃任何寿司。
数据范围
2<=n<=500,p<=1e9
把一个数从它的质因子角度来考虑,对于x,如果x的质因子均不在集合1中,则可以放入集合2中,以此类推
于是我们得到一个初始的,对于n<=30的状压dp:设dp[i][s1][s2]][s2]为当前选到第i个数,集合1的质因子状态为s1,集合2的质因子状态为s2时的方案数
转移显然,考虑加入第i个数时
其中p[i]表示i的质因子集合
发现这个东西其实可以滚动一下,把第一维数组空间省了,然后s1,s2倒着枚举即可
考虑n<=500的情况,事实上我们并不需要对500以内的质因子都状压,因为一个数至多有1个22以上的质因子,所以只需状压sqrt(N)以内的小因子,对于大因子分开讨论dp进去就行
具体的来说,我们记a[i].first为i的大因子,若i所有的因子都<22,则first=1,记a[i].second为i的质因子集合
在dp的时候 所有大因子相同的数,只能放进一个集合中,所以考虑按照大因子的大小排个序,这样就可以把所有大因子相同的数放在一起算
考虑在dp过程中,仍然记dp[s1][s2][s2]为小因子集合为s1,s2的方案数,对于一段大因子相同的数,我们在进入这段时把dp[]拷贝出来,记作g1,g2,g1[s1][s2]表示这段数都放在集合1里,小因子状态s1,s2时的方案数,g2同理
然后在这一段结尾的时候,把这一段的g1和g2合并回到dp里去
注意到在合并前,dp[s1][s2]表示这一段都没选前,状态为s1,s2时的方案数
然后由于g1,g2在拷贝dp时用的都是dp[s1][s2],所以在g1,g2中都有这个“整段没选前”的方案数,所以合并起来时要减掉,即
最后统计答案就
即可。注意取模。看一些大佬存dp时候没开longlong,不知道是不是洛谷题面的锅,题面的模数取值是1e10,实际上好像是1e9
最后上代码
#include<bits/stdc++.h>
using namespace std;
const int prime[8]={2,3,5,7,11,13,17,19};
#define sec second
#define fir first
typedef long long ll;
ll g[4][1<<9][1<<9],dp[1<<9][1<<9];
int bin[30];
pair<int,int> a[510];
int n;
ll p;
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define res(i,x,y) for (int i=x;i>=y;i--)
void prework(){
bin[0]=1;rep(i,1,20) bin[i]=bin[i-1]<<1;
rep(i,2,n){
int x1=i;
rep(j,0,7){
if (x1%prime[j]==0) a[i].sec|=bin[j];
while (x1%prime[j]==0) x1/=prime[j];
}
a[i].fir=x1;
}
sort(a+2,a+1+n);
}
void add(ll &x,ll y){
x+=y;
if (x>=p) x-=p;
}
int main(){
scanf("%d %lld",&n,&p);
prework();
for(int i=2;i<=n;i++) printf("%d
",a[i].fir);
memset(dp,0,sizeof(dp));
dp[0][0]=1;
rep(i,2,n){
if (a[i].fir==1 || a[i].fir!=a[i-1].fir || i==2){
memcpy(g[1],dp,sizeof(g[1]));
memcpy(g[2],dp,sizeof(g[2]));
}
res(s1,255,0)
res(s2,255,0){
if (s1&s2) continue;
if (!(s2&a[i].sec))add(g[1][s1|a[i].sec][s2],g[1][s1][s2]);
if (!(s1&a[i].sec))add(g[2][s1][s2|a[i].sec],g[2][s1][s2]);
}
if ((i==n) || (a[i].fir!=a[i+1].fir) || (a[i].fir==1)){
res(s1,255,0)
res(s2,255,0){
if (s1&s2) continue;
dp[s1][s2]=g[1][s1][s2]+g[2][s1][s2]-dp[s1][s2];
while (dp[s1][s2]>=p) dp[s1][s2]-=p;
while (dp[s1][s2]<0) dp[s1][s2]+=p;
}
}
}
ll ans=0;
rep(s1,0,255) rep(s2,0,255)
if (!(s1&s2) && dp[s1][s2]) add(ans,dp[s1][s2]);
printf("%lld
",ans);
return 0;
}