BZOJ4320 Homework

BZOJ4320 Homework

题目传送门

题解

看了题解之后发现是一个比较妙的分块+离线+并查集的做法。首先我们以30000为界,那么当询问(leq 30000)的时候,我们就可以开个桶暴力进行查找。如果大于30000,那么我们就枚举30000以内(x)的倍数,然后通过并查集找出离这个倍数最近的比它大的值,就是答案。并查集维护的过程可以离线处理,将加点变成删点,每次删点就相当于两个联通块的合并。复杂度即为(O(30000*n))

code

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('
');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=5e5+500;
const int inf=1e9+7;
int n,M;
char opt[2];
int x;
struct query {
	int opt,x;
}q[maxn];
int ans[maxn],mn[maxn],fa[maxn];
/*==================Define Area================*/
int find(int x) {
	return fa[x]==x?x:fa[x]=find(fa[x]);
}

int main() {
	read(n);M=sqrt(300000);
	memset(ans,-1,sizeof ans);
	memset(mn,0x3f,sizeof mn);
	for(int i=0;i<=300000;i++) fa[i]=i+1;
	fa[300001]=300001;
	for(int i=1;i<=n;i++) {
		scanf("%s",opt);
		read(x);
		if(opt[0]=='A') {
			q[i].opt=1;q[i].x=x;
			for(int j=1;j<=M;j++) mn[j]=min(mn[j],x%j);
			fa[x]=x;
		}
		else {
			q[i].opt=2,q[i].x=x;
			if(x<=M) ans[i]=mn[x];
		}
	}
	for(int i=n;i;i--) {
		if(q[i].opt==2&&q[i].x>M) {
			int x=q[i].x;
			int res=inf;
			for(int j=0;j*x<300000;j++) {
				int tmp=find(j*x);
				if(tmp<=300000) res=min(res,tmp%x);
			}
			if(res==inf) res=x;
			ans[i]=res;
		}
		else if(q[i].opt==1) fa[q[i].x]=q[i].x+1;
	}
	for(int i=1;i<=n;i++) {
		if(~ans[i]) printf("%d
",ans[i]); 
	}
	return 0;
}
「我不敢下苦功琢磨自己,怕终于知道自己并非珠玉;然而心中既存着一丝希冀,便又不肯甘心与瓦砾为伍。」
原文地址:https://www.cnblogs.com/Apocrypha/p/9432121.html