Description
为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。
在晚宴上,主办方为大家提供了 n−1 种不同的寿司,编号 1,2,3,…,n−1,其中第 i 种寿司的美味度为 i+1 (即寿司的美味度为从 2 到 n)。
现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为 x 的寿司,小 W 品尝的寿司中存在一种美味度为 y 的寿司,而 x 与 y 不互质。
现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数 p 取模)。注意一个人可以不吃任何寿司。Input
输入文件的第 1 行包含 2 个正整数 n,p,中间用单个空格隔开,表示共有 n 种寿司,最终和谐的方案数要对 p 取模。
Output
输出一行包含 1 个整数,表示所求的方案模 p 的结果。
Sample Input
3 10000
Sample Output
9
HINT
2≤n≤500
0< p≤1000000000
状态压缩DP,我写的
O(n×48)
好像还有一个
O(n×38)
我不会,网上说得简洁,我蒟蒻无比,看不懂,就只会写这个
48
的
然后大概说一下怎么DP
我们选取一个数,实际上就是选它的质因数,而500以内质数有95个明显无法装压,但我们还可以发现,500以内每个数大于
500−−−√
的质因子最多只有一个,而小于
500−−−√
的质数一共只有8个,我们就可以考虑装压了
我们建立两个数组f[k][i][j]表示考虑前k个数,A的状态为i,B的状态为j有多少个情况,dp[0/1][k][i][j]表示正在考虑第k个数,A/B拿走第k个数,拿走后A状态为i,B为j
然后转移方程可以列出:
dp[0][k][i|p(k)][j]=f[k-1][i|p(k)][j]+dp[0][k][i][j]
dp[1][k][i][j|p(k)]=f[k-1][i][j|p(k)]+dp[1][k][i][j]
f[k][i][j]=dp[0][k][i][j]+dp[1][k][i][j]-f[k-1][i][j]
但是数组开不下,我们就可以滚动,这时才有了我一直没看懂的转移方程
dp[0][i|p(k)][j]=dp[0][i|p(k)][j]+dp[0][i][j]
dp[1][i][j|p(k)]=dp[1][i][j|p(k)]+dp[1][i][j]
f[i][j]=dp[0][i][j]+dp[1][i][j]-f[i][j]
然后我们先处理前面全部属于那8个质数的,再一个一个大质数的枚举,处理就可以了
程序如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define INF 2100000000
#define ll long long
#define clr(x) memset(x,0,sizeof(x))
#define clrmax(x) memset(x,127,sizeof(x))
using namespace std;
inline int read()
{
char c;
int ret=0;
while(!(c>='0'&&c<='9'))
c=getchar();
while(c>='0'&&c<='9')
{
ret=(c-'0')+(ret<<1)+(ret<<3);
c=getchar();
}
return ret;
}
#define M 500
#define N (1<<8)
int f[N][N],dp[2][N][N],n,P,t;
const int pri[]={2,3,5,7,11,13,17,19};
struct node
{
int x,num;
node(int x=0,int num=0):x(x),num(num){}
}a[M];
#define x(y) a[y].x
#define num(x) a[x].num
bool com(node a,node b)
{
return a.x<b.x;
}
void add(int x)
{
int temp=0;
for(int i=0;i<8;i++)
while(x%pri[i]==0)
{
x/=pri[i];
temp|=1<<i;
}
a[++t]=node(x,temp);
}
void DP()
{
f[0][0]=1;
for(int l=1,r;l<=t;l=r+1)
{
for(r=l;x(r+1)==x(r)&&x(r)!=1;r++);
memcpy(dp[0],f,sizeof(f));memcpy(dp[1],f,sizeof(f));
for(int k=l;k<=r;k++)
{
for(int i=255;~i;i--)
{
int now=255^i;
for(int j=now;;j=(j-1)&now)
{
if((j&num(k))==0)
dp[0][i|num(k)][j]=(dp[0][i|num(k)][j]+dp[0][i][j])%P;
if((i&num(k))==0)
dp[1][i][j|num(k)]=(dp[1][i][j|num(k)]+dp[1][i][j])%P;
if(!j)break;
}
}
}
for(int i=0;i<=255;i++)
for(int j=0;j<=255;j++)
f[i][j]=((dp[0][i][j]+dp[1][i][j]-f[i][j])%P+P)%P;
}
int ans=0;
for(int i=0;i<=255;i++)
{
int now=255^i;
for(int j=now;;j=(j-1)&now)
{
ans=(ans+f[i][j])%P;
if(!j)break;
}
}
printf("%d",ans);
}
int main()
{
//freopen("in.txt","r",stdin);
n=read();P=read();
for(int i=2;i<=n;i++)
add(i);
sort(a+1,a+t+1,com);
DP();
return 0;
}
大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。