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;
}
时间大概几千万的样子,可以过。(用线筛)