3318. 【BOI2013】Brunhilda的生日 (Standard IO)

本文介绍了一个游戏中的策略问题,目标是最少次数地宣读数字,使所有参与游戏的孩子被淘汰。通过动态规划的方法,计算出在给定一系列质数的情况下,对于不同数量的孩子,如何快速结束游戏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Time Limits: 1000 ms  Memory Limits: 262144 KB  Detailed Limits  

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

Source / Author: BOI2013 brunhilda

 

题解:

考虑DP,设f[i] 为 有i个孩子时的最小代价。

f[i] = min(f[i - i%pj] ) + 1;

拿样例来说。

比如说当i = 5 , p = {2,3} 时 , f[i] = min(f[3] , f[4]) + 1 = 3 (f[3] = 2 , f[4]=3) 

因为当 i 减去了i % pj后 , 就是pj的倍数了(3是3的倍数 , 4是2的倍数) ,此时相当于pj的倍数多出了一点余数(i%pj),再用pj“筛”多一次即可。

 

显然f是单调不降的 , 所以 让i - i%pj越小越好。

考虑记录一个ex[i] 表示f[i+1] ~f[ i+ex[i]-1]都是f[i]+1

比如初始化时,ex[0] = 最大的质数 ,因为 1~最大的质数-1 都是可以一次“筛”掉的。

再比如ex[p[i]] = p[i] , 因为 p[i]+1 ~ p[i]+p[i]-1都可以在用p[i]“筛”了一次后再筛一次搞定。即f[p[i] +1~ p[i]*2-1] = f[p[i]]+1.

那么其他的ex[i]怎么求呢?

设读入的质数表 p[]

首先要知道,当i不是任何p[j]的倍数,则ex[i]=0

所以i一定是某p的倍数,由于要求ex最大,我们预处理出i在p中最小的质因子minp , 然后ex[i] = max(ex[minp] , ex[i/minp])

ex[minp]一定不为0 , 而ex[i/minp]可能为0,但当i / minp也是p的元素,ex[i/minp] 就>= ex[minp]了。

然后我们逐个求f,对于i位置,找到一个 j 时j+ex[j]恰不小于i (为什么让j尽量小之前解释过了)f[i] = f[j+1] ,更新ex[i]

注意:

预处理出i在p中最小的质因子minp,为了方便,可以直接预处理出i的最小的质因子

(如果i是一个p的倍数,i/minp是也是那个p的倍数,或者刚好i的最小质因子就在p[]中)

(如果i不是p的倍数,minp和 i/minp也不是p的倍数,ex[i]=0,这也验证了上文“首先要知道,当i不是任何p[j]的倍数,则ex[i]=0”)

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define mcy(a,b) memcpy(a,b,sizeof(a))
#define ll long long
#define inf 2147483647
#define N 100010
#define P 10000010
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
#define INF 2147483647
using namespace std;


int m,Q,i,j,t,ans,maxn;
int f[P],minp[P],p[N],pri[P],q[N],ex[P];

void init()
{
	minp[1]=1;
	for(int i=2;i<=maxn;i++)
	{
		if(minp[i]==0)
		{
			pri[++pri[0]] = i;
			minp[i]=i;
		}
		for(int j=1;j<=pri[0];j++)
		{
			int k = i* pri[j];
			if(k > maxn || pri[j] > minp[i]) break;
			minp[k] = pri[j];
		}
	}
	return ;
}

int main()
{
	scanf("%d%d",&m,&Q);
	for(i=1;i<=m;i++) scanf("%d",&p[i]);
	for(i=1;i<=Q;i++)scanf("%d",&q[i]),maxn = max(maxn , q[i]);

	init(); //minp pri 	
	mem(ex,0);
	ex[0] = p[m];
	for(i=1;i<=m;i++) ex[p[i]] = p[i];
	f[0] = 0;
	j=0;
	maxn++;
	for(i=1;i<maxn;i++)
	{
		while(j + ex[j]   <= i || ex[j]==0)
		{
			++j;
			if(i==j) break;
		}
		if(i==j) 
			{maxn = i; break;}
		f[i] = f[j] + 1;
		ex[i] = max(ex[i/minp[i]] , ex[minp[i]]);
	}
	
	for(i=1;i<=Q;i++)
		if(q[i] >= maxn) printf("oo\n"); else printf("%d\n",f[q[i]]);
	return 0;
}

时间大概几千万的样子,可以过。(用线筛)

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值