寿司晚宴

link

一道很有价值的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;
}
posted @ 2022-07-02 15:03  Feyn618  阅读(29)  评论(0编辑  收藏  举报