题目描述:
除去对铁质盔甲强烈的热爱,Brunhilda是一个正常的7岁女孩。近期,她正在策划一个完美的生日派对。她发明了如下的一个游戏:所有的孩子在一个数k被宣读之前不停地跑来跑去。当这个数字k宣读后,所有的孩子将形成人数恰好为k的若干群体,且保证剩余的孩子数目小于k。最后,这不足k个的孩子将从游戏中被淘汰。紧接着,比赛将继续进行,并公布一个新的数字k。游戏将在所有的孩子都被淘汰后结束。
Brunhilda请她的父亲Wotan在游戏中来宣读数字。Wotan不喜欢这个游戏,当然也不希望在游戏的第一轮就宣布一个正无穷(PS:宣布正无穷等于将所有的孩子都从游戏中淘汰)。 Brunhilda认为这在派对上是相当尴尬的情形,所以她给了她父亲一串共计m个素数的列表。这样,她的父亲便可以从中进行选择。当然,相同的数字在游戏中可以被多次宣读。
Wotan想尽快结束比赛,因为他有一张他最喜欢的足球俱乐部 FC Asgard的比赛门票。不幸的是,Brunhilda不知道派对上参加游戏的孩子数目。现在,对于Q个不同的数n1,...,nQ个儿童,Wotan要预先知道他所需宣读的最少数字,以便他尽早结束游戏。
第一行包含整数m和Q。
第二行包含m个不同的递增素数pi(1≤i≤M),表示Wotan可以宣读的数字。
接下来Q行分别包含一个整数nj(1≤j≤Q),表示可能参加游戏的孩子数目nj。
输出包括Q行。第j行表示对于询问nj所得到的答案,即如果Wotan能结束游戏,请输出他最少所需要宣读的数字个数,否则输出字符串oo(两个小写字母o表示∞)。
我们可以先考虑一下,如果儿童数有5,此时有2,3两种命令,就可以有几种情况:
5-5%3=3,3-3%2=2,2-2%3=0;
5-5%2=4,4-4%3=3,3-3%2=2,2-2%3=0;
总之最小情况是3.
不难发现,每次操作可以变成一个公式:
设有n个儿童,则为n-n%p[j]。
我们要求的就是这个方程:
时间复杂度:O(NM),考虑优化:
1.函数单调不减,所以要满足(x-x mod p[j])越小越好.
设x-x mod p[j]=k,令 x=ap[j]+b,则原式化为:
ap[j]+b-(ap[j]+b) mod p[j]=k.
ap[j]+b-b=k.
ap[j]=k.
所以p[j]为k的一个质因数。
所以k如果不是p[j]的倍数的话是无法推出下一个答案的.
同时,设下一个状态为y,对于每一个y,y<x+p[j],必须同时满足上面两个条件才能由f(x)转移到f(y)。
对于每一个y,其对应的x唯一。
所以维护p[i]对应的x,用堆来维护它们的最小值。
O(N*log2M)
2.维护最小值的另外一种方法:
对于每一个x,只要有一个p[j]可以使它不失效它就不会失效,所以p[j]越大越好(就能保证有更大的范围选出最小值),用线性筛求出每个数s最大时p[i]的倍数,设其为max_prime(q),若s=tk,则max_prime=max(k,max_prime(t)),接着直接维护有效的最小可转移状态。
O(N)
3.图像分析:
如果画成图像,实际上可以看出这是一个区间为[i+1->i+p[j]-1]的阶梯递增函数,而每个阶梯的状态都由其上一个阶梯中prime最大值决定,所以端点值由上端转移而来,找到这个f[y]=f[x]+1.
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF=1e9; 4 const int N=1e5+10; 5 const int M=1e7+10; 6 int n,c; 7 int p[N]; 8 int child[N],f[M],q[M],num[M]; 9 int maxn; 10 int main(){ 11 cin>>n>>c; 12 for(int i=1;i<=n;i++){ 13 scanf("%d",&p[i]); 14 } 15 for(int i=1;i<=c;i++){ 16 scanf("%d",&child[i]); 17 maxn=max(maxn,child[i]); 18 } 19 for(int i=1;i<=maxn;i++){ 20 f[i]=num[i]=INF; 21 } 22 for(int i=1;i<=n;i++){ 23 for(int j=p[i];j<=maxn;j+=p[i]){ 24 num[j]=p[i]; 25 } 26 } 27 int l=1,r=2,st=1;q[1]=0;f[0]=0;num[0]=p[n]; 28 while(l<r&&st<=maxn){//队列维护 29 int x=q[l];l++; 30 if(num[x]==INF) continue; 31 int tmp=min(x+num[x]-1,maxn); 32 for(;st<=tmp;st++){ 33 f[st]=f[x]+1; 34 q[r++]=st; 35 } 36 } 37 for(int i=1;i<=c;i++) 38 { 39 if(f[child[i]]==INF)printf("oo\n"); 40 else printf("%d\n",f[child[i]]); 41 } 42 return 0; 43 }