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