bzoj4197:[Noi2015]寿司晚宴

传送门

又做一次题解的搬运工,神仙题
分解质因数很显然是能想到的,不过纯粹的分解质因数只能得到可惜的30分
可以发现一个小于\(500\)的数,大于\(22\)的质因数最多只有\(1\)
所以我们就可以将小于\(22\)的质数状压起来
然后对于大于\(22\)的质数进行讨论
可以知道对于一段大于\(22\)的质数相同的情况,他们都只能给其中的一个人,然后前面的可以刷表法转移
然后空间开不下,需要滚动数组,
具体的状态转移可以看代码
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
void read(int &x) {
	char ch; bool ok;
	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=510,maxm=(1<<8)+10;
int n,mx=1<<8,d[8]={2,3,5,7,11,13,17,19};
long long ans,p,f1[maxm][maxm],f2[maxm][maxm],g[maxm][maxm];
struct oo{int x,y;}a[maxn];
void prepare(int x)
{
	int now=x+1;a[x].y=-1;
	for(rg int i=0;i<8;i++)
		if(!(now%d[i]))
		{
			a[x].x|=(1<<i);
			while(!(now%d[i]))now/=d[i];
		}
	if(now!=1)a[x].y=now;
}
bool cmp(oo a,oo b){return a.y<b.y;}
int main()
{
	read(n),scanf("%lld",&p);
	for(rg int i=1;i<n;i++)prepare(i);
	sort(a+1,a+n,cmp),g[0][0]=1;
	for(rg int i=1;i<n;i++)
	{
		if(i==1||a[i].y!=a[i-1].y||a[i].y==-1)memcpy(f1,g,sizeof g),memcpy(f2,g,sizeof g);
		for(rg int j=mx-1;j>=0;j--)
			for(rg int k=mx-1;k>=0;k--)
				if(!(j&k))
				{
					if(!(a[i].x&j))f2[j][k|a[i].x]=f2[j][k]+f2[j][k|a[i].x]>=p?f2[j][k]+f2[j][k|a[i].x]-p:f2[j][k]+f2[j][k|a[i].x];
					if(!(a[i].x&k))f1[j|a[i].x][k]=f1[j][k]+f1[j|a[i].x][k]>=p?f1[j][k]+f1[j|a[i].x][k]-p:f1[j][k]+f1[j|a[i].x][k];
				}
		if(i==n-1||a[i].y!=a[i+1].y||a[i].y==-1)
		{
			for(rg int j=mx-1;j>=0;j--)
				for(rg int k=mx-1;k>=0;k--)
					if(!(j&k))g[j][k]=(((f1[j][k]+f2[j][k]-g[j][k])%p)+p)%p;
		}
	}
	for(rg int j=mx-1;j>=0;j--)
		for(rg int k=mx-1;k>=0;k--)
			if(!(j&k))ans=ans+g[j][k]>=p?ans+g[j][k]-p:ans+g[j][k];
	printf("%lld\n",ans);
}
posted @ 2019-03-24 21:56  蒟蒻--lichenxi  阅读(124)  评论(0编辑  收藏  举报