3318. 【BOI2013】Brunhilda的生日

64 篇文章 0 订阅
63 篇文章 0 订阅

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值