正题
题目大意
序列aaa有mmm个质数。然后询问一个数nnn,每次可以使n=n−n%ain=n-n\%a_{i}n=n−n%ai
求最少操作次数。
解题思路
首先我们设fif_ifi表示由iii变为0的最少次数,然后有动态转移fi=min{fi−i%aj}+1f_i=min\{f_{i-i\%a_j}\}+1fi=min{fi−i%aj}+1
然后我们会发现fif_ifi的具有单调性,证明:
n−n%ai=ai⌊nai⌋n-n\%a_{i}=a_i\lfloor \frac{n}{a_i}\rfloorn−n%ai=ai⌊ain⌋
假设fff单调不降,i=1i=1i=1时显然成立
ai⌊kai⌋a_i\lfloor \frac{k}{a_i}\rfloorai⌊aik⌋和ai⌊k+1ai⌋a_i\lfloor \frac{k+1}{a_i}\rfloorai⌊aik+1⌋比较
∵k+1>k\because k+1>k∵k+1>k
那么就有⌊k+1ai⌋≥⌊kai⌋\lfloor \frac{k+1}{a_i}\rfloor\geq \lfloor \frac{k}{a_i}\rfloor⌊aik+1⌋≥⌊aik⌋
因为fff单调不降
f⌊k+1ai⌋≥f⌊kai⌋f_{\lfloor \frac{k+1}{a_i}\rfloor}\geq f_{\lfloor \frac{k}{a_i}\rfloor}f⌊aik+1⌋≥f⌊aik⌋
即fk+1≥fkf_{k+1}\geq f_{k}fk+1≥fk
证毕
我们发现每一个fif_ifi能更新的序列都是一段连续的序列,且是位置单调不降的。那么我们考虑计算每个fif_ifi能更新的区间。
设fxf_xfx能更新fkf_kfk,那么有k≤x+numx−1k\leq x+num_x-1k≤x+numx−1因为若能更新那么有k−k%ai=ik-k\%a_i=ik−k%ai=i且对于kkk来说xxx是最大的,那么有numxnum_xnumx就是其中的aia_iai。
但若k=x+numxk=x+num_xk=x+numx那么x+numi−(x+numi)%numi=x+numix+num_i-(x+num_i)\%num_i=x+num_ix+numi−(x+numi)%numi=x+numi那么就有更优的方案就不是xxx了。
codecodecode
#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int M=10000001,N=101000;
int m,n,a[N],f[M],Q,num[M],que[N],maxn;
queue<int> q;
int main()
{freopen("data.in","r",stdin);freopen("data.out","w",stdout);scanf("%d%d",&m,&Q);for(int i=0;i<m;i++)scanf("%d",&a[i]);for(int i=1;i<=Q;i++)scanf("%d",&que[i]),maxn=max(maxn,que[i]);memset(f,0x3f,sizeof(f));memset(num,0x3f,sizeof(num));for(int i=0;i<m;i++)for(int j=a[i];j<=maxn;j+=a[i])num[j]=a[i];q.push(0);f[0]=0;num[0]=a[m-1];int now=1;while(!q.empty()&&now<=maxn){int x=q.front();q.pop();if(num[x]>=2147483647/3) continue;int tmp=min(x+num[x]-1,maxn);for(;now<=tmp;now++)f[now]=f[x]+1,q.push(now);}for(int i=1;i<=Q;i++){if(f[que[i]]>=2147483647/3) printf("oo\n");else printf("%d\n",f[que[i]]);}
}