Description
给定m个素数和Q个询问。每个询问有n个人,每次操作可以任意选择其中的一个素数p(素数可以重复使用),然后去掉剩余人数 mod p个人。对于每个询问,我们想知道,至少需要多少步操作才能去掉所有人。
Solution
这题的一个想法是
f
i
f_i
fi表示
i
i
i这个数的最少步数,但是这样要做到
O
(
1
)
O(1)
O(1)转移。其实决策点是单调的,
f
f
f也是单调不降的,所以可以一边扫一边移动决策点,一个
j
j
j能够成为
i
i
i的决策点要满足
i
−
j
<
v
[
j
]
i-j<v[j]
i−j<v[j],
v
[
j
]
v[j]
v[j]表示
j
j
j的最大在
m
m
m个给出质数中的质因数,这个可以通过线性筛预处理。
注意由于空间限制小,有些数组要共用。
Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=10000010,Maxm=100010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int g[Maxn],f[Maxn],len=0,p[Maxm],n,m,q,v[Maxn],qi[Maxm];
void pre(int N)
{
g[1]=1;
for(int i=2;i<=N;i++)
{
if(!v[i])f[++len]=i,g[i]=i;
for(int j=1;j<=len&&f[j]*i<=N;j++)
{
v[f[j]*i]=1;
g[f[j]*i]=f[j];
if(i%f[j]==0)break;
}
}
}
int main()
{
m=read(),q=read();int mx=0,mxp=0;
for(int i=1;i<=m;i++)mxp=max(mxp,p[i]=read());
for(int i=1;i<=q;i++)mx=max(mx,qi[i]=read());
pre(mx);
for(int i=1;i<=mx;i++)v[i]=0;
for(int i=1;i<=m;i++)v[p[i]]=p[i];
for(int i=1;i<=mx;i++)v[i]=max(v[i/g[i]],v[g[i]]);
for(int i=0;i<mxp;i++)f[i]=1;int j=1;
for(int i=mxp;i<=mx;i++)
{
if(i-j<v[j]){f[i]=f[j]+1;continue;}
while(j<i&&i-j>=v[j])j++;
if(i==j)break;
f[i]=f[j]+1;
}
for(int i=1;i<=q;i++)
{
if(f[qi[i]]==0)putchar('o'),putchar('o'),putchar('\n');
else printf("%d\n",f[qi[i]]);
}
}