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]);
}
posted @ 2020-08-26 10:16  INFP  阅读(140)  评论(0编辑  收藏  举报