「NOI2015」寿司晚宴
这个题思路其实挺自然的,但是我太傻了...最开始想着钦定一些,结果发现假了..
首先一个比较套路的事情是状压前8个质数,后面的只会在一个数出现一次的再想办法就好。
然后发现有个重要的事情是后面每个质因子\(x\)做统计的时候都是独立的,那么单独做就好了
显然要压两个人的前面质因子集合\(f_{i,j}\)代表两个人分别是\(i,j\)集合的答案,然后一块一块的加后面的质因子就好
加每一块时,我们显然需要处理谁选择了这一块或者都没选,再搞个\(dp_{0/1,i,j}\)钦定一下谁选,随便转移一下就好了
Code:
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
template <class T>
void read(T &x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
const int N=510;
const int pri[9]={0,2,3,5,7,11,13,17,19};
int n,m,p,dp[2][1<<8][1<<8],f[1<<8][1<<8];
inline int add(int a,int b){return a+b>=p?a+b-p:a+b;}
inline int mul(int a,int b){return 1ll*a*b%p;}
struct num
{
int s,pri;
bool friend operator <(num a,num b){return a.pri<b.pri;}
}yuu[N];
int main()
{
read(n),read(p);
for(int i=2;i<=n;i++)
{
yuu[i].pri=i;
for(int j=1;j<=8;j++)
if(i%pri[j]==0)
{
yuu[i].s|=1<<j-1;
while(yuu[i].pri%pri[j]==0) yuu[i].pri/=pri[j];
}
}
std::sort(yuu+2,yuu+n+1);
f[0][0]=1;int U=(1<<8)-1;
for(int i=2;i<=n;i++)
{
if(yuu[i].pri!=yuu[i-1].pri||yuu[i].pri==1)
{
memcpy(dp[0],f,sizeof f);
memcpy(dp[1],f,sizeof f);
}
int sta=yuu[i].s;
for(int s=U;~s;s--)
for(int t=U;~t;t--)
if(!(s&t))
{
if(!(t&sta)) dp[0][s|sta][t]=add(dp[0][s|sta][t],dp[0][s][t]);
if(!(s&sta)) dp[1][s][t|sta]=add(dp[1][s][t|sta],dp[1][s][t]);
}
if(yuu[i].pri!=yuu[i+1].pri||yuu[i].pri==1)
{
for(int s=0;s<=U;s++)
for(int t=0;t<=U;t++)
if(!(s&t))
f[s][t]=add(add(dp[0][s][t],dp[1][s][t]),p-f[s][t]);
}
}
int ans=0;
for(int s=0;s<=U;s++)
for(int t=0;t<=U;t++)
ans=add(ans,f[s][t]);
printf("%d\n",ans);
return 0;
}
2019.3.4