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
",ans[i]);
}
原文地址:https://www.cnblogs.com/shikeyu/p/13563525.html