寿司晚宴
一道很有价值的DP题,而且运用了根号分治的思想。
假如数据范围不大,就像sub1一样,可以直接把可能出现的所有质数压到一个int上之后,用 f[i][j] 来代表第一个人吃了集合i的质数,第二个人吃了集合j的质数时的方案数。使用刷表法从前往后更新,每个数有选和不选两种决策,注意取模和龙龙宝宝即可。
但题目中数据来到了500,质数个数也接近100,状压显然就压不下了。但我们知道一个重要的性质,即对于一个数 \(N\) ,它超过 \(\sqrt{N}\) 的因子个数只能有一个。所以考虑根号分治。前8个质数和前面的想法一样压进一个int里,然后对于剩下的这些大质数进行分类,显然大质数相同的寿司只能被同一个人选择,所以对于一些大质数相同的寿司单独做一次DP,最后把答案累加进答案数组即可。
挺好写的。
#include<bits/stdc++.h>
//#define zczc
#define int long long
const int N=510;
const int S=(1<<8);
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
int m,mod,p[9]={0,2,3,5,7,11,13,17,19};
inline void add(int &s1,int s2){
s1+=s2;s1%=mod;return;
}
struct node{
int big,small;
}a[N];
inline bool cmp(node s1,node s2){
return s1.big<s2.big;
}
int f[2][S][S],g[2][S][S],an[S][S];
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);read(mod);
for(int i=2;i<=m;i++){
int now=i;
for(int j=1;j<=8;j++){
if(now%p[j])continue;
a[i].small|=(1<<j-1);
while(now%p[j]==0)now/=p[j];
}
a[i].big=now;
}
sort(a+2,a+m+1,cmp);
f[0][0][0]=1;
int i=2;
for(;i<=m&&a[i].big==1;i++){
int now=i&1,nxt=i+1&1;
memset(f[nxt],0,sizeof(f[nxt]));
for(int j=0;j<S;j++){
for(int k=0;k<S;k++){
if(j&k)continue;
add(f[nxt][j][k],f[now][j][k]);
int ss=a[i].small;
if(((j|ss)&k)==0)add(f[nxt][(j|ss)][k],f[now][j][k]);
if(((k|ss)&j)==0)add(f[nxt][j][(k|ss)],f[now][j][k]);
}
}
}
/*
for(int j=0;j<4;j++){
for(int k=0;k<4;k++)printf("%d ",f[(i&1)][j][k]);
printf("\n");
}
printf("\n");
*/
for(int s1=0;s1<S;s1++){
for(int s2=0;s2<S;s2++){
an[s1][s2]=f[(i&1)][s1][s2];
}
}
for(;i<=m;){
//printf("ss=%lld\n",a[i].big);
int j=0;
for(int j=0;j<S;j++){
for(int k=0;k<S;k++){
f[0][j][k]=g[0][j][k]=an[j][k];
}
}
for(;a[i+j].big==a[i].big;j++){
int now=j&1,nxt=j+1&1;
memset(f[nxt],0,sizeof(f[nxt]));
memset(g[nxt],0,sizeof(g[nxt]));
for(int s1=0;s1<S;s1++){
for(int s2=0;s2<S;s2++){
if(s1&s2)continue;
add(f[nxt][s1][s2],f[now][s1][s2]);
add(g[nxt][s1][s2],g[now][s1][s2]);
int ss=a[i+j].small;
if(((s1|ss)&s2)==0)add(f[nxt][(s1|ss)][s2],f[now][s1][s2]);
if(((s2|ss)&s1)==0)add(g[nxt][s1][(s2|ss)],g[now][s1][s2]);
}
}
}
for(int s1=0;s1<S;s1++){
for(int s2=0;s2<S;s2++){
an[s1][s2]=f[(j&1)][s1][s2]+g[(j&1)][s1][s2]-an[s1][s2];
an[s1][s2]%=mod;
}
}
i+=j;
}
int ans=0;
for(int s1=0;s1<S;s1++){
for(int s2=0;s2<S;s2++){
ans=(ans+an[s1][s2])%mod;
}
}
printf("%lld",(ans%mod+mod)%mod);
return 0;
}
一如既往,万事胜意