Description
除去对铁质盔甲强烈的热爱,Brunhilda是一个正常的7岁女孩。近期,她正在策划一个完美的生日派对。她发明了如下的一个游戏:所有的孩子在一个数k被宣读之前不停地跑来跑去。当这个数字k宣读后,所有的孩子将形成人数恰好为k的若干群体,且保证剩余的孩子数目小于k。最后,这不足k个的孩子将从游戏中被淘汰。紧接着,比赛将继续进行,并公布一个新的数字k。游戏将在所有的孩子都被淘汰后结束。
Brunhilda请她的父亲Wotan在游戏中来宣读数字。Wotan不喜欢这个游戏,当然也不希望在游戏的第一轮就宣布一个正无穷(PS:宣布正无穷等于将所有的孩子都从游戏中淘汰)。 Brunhilda认为这在派对上是相当尴尬的情形,所以她给了她父亲一串共计m个素数的列表。这样,她的父亲便可以从中进行选择。当然,相同的数字在游戏中可以被多次宣读。
Wotan想尽快结束比赛,因为他有一张他最喜欢的足球俱乐部 FC Asgard的比赛门票。不幸的是,Brunhilda不知道派对上参加游戏的孩子数目。现在,对于Q个不同的数n1,...,nQ个儿童,Wotan要预先知道他所需宣读的最少数字,以便他尽早结束游戏。
Input
第一行包含整数m和Q。
第二行包含m个不同的递增素数pi(1≤i≤M),表示Wotan可以宣读的数字。
接下来Q行分别包含一个整数nj(1≤j≤Q),表示可能参加游戏的孩子数目nj。
Output
输出包括Q行。第j行表示对于询问nj所得到的答案,即如果Wotan能结束游戏,请输出他最少所需要宣读的数字个数,否则输出字符串oo(两个小写字母o表示∞)。
Sample Input
2 2
2 3
5
6
Sample Output
3
oo
Data Constraint
20%的数据:m,nj,Q<=10000.
另有20%的数据:Q=1
100%的数据:1<=m,Q<=100 000,1<=nj<=10 000 000,2<=pi<=10 000 000
Solution
dp。设f[i]表示人数为i时最小宣读数字个数,转移只需要枚举j,f[ i ]=min(f[ i ],f[ i - i%p[ j ]+1 ])。但是这样时超。
考虑优化。
显然当i>p[1]*p[2]*...*p[m]时,f[i]=oo
然而没什么用。
我们可以发现f数组是单调不下降的,并且考虑一个 i,它可以由 i - i%p 转移过来,反过来,设k= i-i%p,则有k为p的倍数,k可以转移到k+i%p,因为i%p取0~p-1,所以k实际上可以转移到k+1~k+p-1。所以答案应该是连续的一段1,然后是连续一段2,以此类推。因此,我们需要在做答案是1的那一段时将答案为2的那一段的右端点求出来,然后一段一段做就可以了。若要求下一段的右端点,我们可以预处理出p数组,p[i]表示i的最大的在给定的素数中出现过的素因子。那么下一段的右端点即为max{i+p[i]-1}。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define F(i,a,b) for(int i=a;i<=b;i++)
#define N 10000000
using namespace std;
int f[N+10],p[N+10],n,m,x,l,r,t,tot=0;
int main(){
scanf("%d%d",&n,&m);
F(i,1,n){
scanf("%d",&x);r=x-1;
for(int j=x;j<=N;j+=x) p[j]=x;
}
l=1;
while(l<=r&&r<=N){
tot++;
F(i,l,r){
f[i]=tot;
t=max(t,i+p[i]-1);
}
t=min(t,N);
l=r+1;r=t;
}
while(m--){
scanf("%d",&x);
if(!f[x]) printf("oo\n");else printf("%d\n",f[x]);
}
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/95102501