BZOJ 4320
\(bzoj4320~~Homework\)
\(1:\)在人物集合 S 中加入一个新的程序员,其代号为\(X\),保证\(X\)在当前集合中不存在。
\(2:\)在当前的人物集合中询问程序员的\(mod~Y\) 最小的值。 (为什么统计这个?因为拯救
过世界的人太多了,只能取模)
其中 对于 100%的数据:\(N≤100000, 1≤X,Y≤300000,\)保证第二行为操作 1。
好神的题:根号分治+并查集
设\(N\)表示\(x\)的最大值,考虑对权值序列分治,当模数不超\(\sqrt N\),开\(\sqrt N\)个桶,每次暴力更新桶,\(O(1)\)求答案
当模数超过\(\sqrt N\)时,它在\(n\)之内的倍数不超过\(\sqrt N\)个, ,我们离线处理。把加点改成删点。 每次询问相当于询问当前数集中比Y或Y的倍数大并且距离最近的数。删点很显然可以用并查集处理,把空格都连起来(同花神游历各国)。
#include<bits/stdc++.h>
using namespace std;
int n,siz,m,q[100010],f[300001];
int ans[100010],st[610];
bool vis[300010]; char str[10];
int find(int x){return f[x] == x ? x : f[x] = find(f[x]);}
int main(){
scanf("%d",&n); siz = 550;
int i,j; memset(st,0x3f,sizeof(st));
for(i = 1;i <= n;++i){
scanf("%s%d",str,&q[i]);
if(str[0] == 'A'){
for(int j = 1;j <= siz;++j) st[j] = min(st[j],q[i] % j);
m = max(m,q[i]),vis[q[i]] = 1,q[i] = -q[i];
}
else if(q[i] <= siz) ans[i] = st[q[i]];
}
for(i = 0;i <= m+1;++i) f[i] = (vis[i]) ? i : i+1;
for(i = n;i >= 1;--i){
if(q[i] < 0) f[-q[i]] = find(-q[i]+1);
else{
if(q[i]>siz){
ans[i]=1 << 30;
for(j = 0;j <= m;j += q[i]) ans[i]=min(ans[i],find(j)%q[i]);
}
}
}
for(i=1;i<=n;i++) if(q[i]>0) printf("%d\n",ans[i]);
}