BZOJ4320 : ShangHai2006 Homework
取$M=\sqrt{300000}$。
设$g[i]$表示程序员的$\bmod i$最小的值。
若$Y<M$,那么可以在$O(M)$时间内完成对所有$g[i]$的修改,$O(1)$时间内完成查询。
若$Y\geq M$,那么枚举$Y$的倍数,等价于查询一段区间内的最小值,可以通过分块做到$O(M)$修改,$O(1)$查询。
因为倍数不超过$M$个,所以询问的总复杂度为$O(M)$。
所以总时间复杂度为$O(n\sqrt{300000})$。
#include<cstdio> const int N=300010,M=550; int n=300000,m=n/M,q,i,x,pos[N],st[M],f[N],tag[M],g[M];char op[5]; inline void up(int&a,int b){if(a>b)a=b;} inline void add(int x){ for(int i=1;i<M;i++)up(g[i],x%i); for(int i=st[pos[x]];i<=x;i++)up(f[i],x); for(int i=pos[x]-1;~i;i--)up(tag[i],x); } inline int ask(int x){ if(!x)x++; int t=f[x]; up(t,tag[pos[x]]); return t; } inline int query(int x){ if(x<M)return g[x]; int t=N; for(int i=0,j=x;i<=n;i=j,j+=x){ if(j>n)j=n+1; int y=ask(i); if(y<j)up(t,y-i); } return t; } int main(){ for(i=1;i<=n;i++)pos[i]=i/M; for(i=n;i;i--)st[pos[i]]=i; for(i=1;i<=n;i++)f[i]=N; for(i=0;i<=m;i++)tag[i]=N; for(i=1;i<M;i++)g[i]=N; scanf("%d",&q); while(q--){ scanf("%s%d",op,&x); if(op[0]=='A')add(x);else printf("%d\n",query(x)); } return 0; }