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;
}