BZOJ4320 ShangHai2006 Homework(分块+并查集)
考虑根号分块。对于<√3e5的模数,每加入一个数就暴力更新最小值;对于>√3e5的模数,由于最多被分成√3e5块,查询时对每一块找最小值,这用一些正常的DS显然可以做到log,但不太跑得过。考虑并查集在序列上的奇技淫巧。加点不太能做,考虑离线改成删点。并查集维护下一个未删除的点即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 100010 #define M 300000 int m,n,q,a[N],b[N],mn[N],ans[N],fa[N*3]; bool flag[N*3]; const int block=600; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} int main() { #ifndef ONLINE_JUDGE freopen("bzoj4320.in","r",stdin); freopen("bzoj4320.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif m=read(); for (int i=1;i<=block;i++) mn[i]=i; while (m--) { char c=getchar();while (c!='A'&&c!='B') c=getchar(); a[++n]=read(); if (c=='A') { b[n]=1;flag[a[n]]=1; for (int i=1;i<=block;i++) mn[i]=min(mn[i],a[n]%i); } else if (a[n]<=block) ans[n]=mn[a[n]]; } fa[M+1]=M+1; for (int i=M;i>=0;i--) if (flag[i]) fa[i]=i;else fa[i]=fa[i+1]; for (int i=n;i>=1;i--) if (b[i]) fa[a[i]]=find(a[i]+1); else if (a[i]>block) { ans[i]=M+1; for (int j=0;j<=M;j+=a[i]) { int p=find(j); if (p<min(j+a[i],M+1)) ans[i]=min(ans[i],p%a[i]); } } for (int i=1;i<=n;i++) if (!b[i]) printf("%d\n",ans[i]); return 0; }