Educational Codeforces Round 72 F. Forced Online Queries Problem
题目梗概
有一张\(n\)个点的图,刚开始没有边,现在又两种操作,一种是加入一条边(如果这条边存在,否则删去这条边),一种是询问\(x,y\)是否联通。
\(x,y\)给出的形式是\((x+last-1)%n+1\),\((y+last-1)%n+1\),\(last\)为上一次询问的答案。
解题思路
对于这题的离线版本有两种写法:线段树分治,以及对操作分块。
线段树分治的做法比较常见,下面来讲讲分块的做法。
对于当前块之前的操作,我们可以找出哪些边在当前块需要操作,设为集合\(B\),当然就有不需要操作的边,设为集合\(C\)。
对于集合\(C\)我们可以提前建好边,然后对于当前块的操作:如果是修改,那么直接修改集合\(B\);如果是询问,就往集合\(C\)暴力加入集合\(B\)的边,然后查询。
因为集合\(C\)的构建次数不超过\(m/P\),集合\(B\)的大小不超过\(P\),所以复杂度为\(m\sqrt mlogm\)。
那么回到这道题,分块的做法十分的显然,集合\(B\)的定义改成可能操作的边的集合即可。
其实线段树分治也是可以的,因为分治的过程也是按时间顺序,我们考虑这条边可能加入的位置。
如果这条边是第奇数次确定加入,那么就可以贡献到下次可能出现的位置,直到某个位置也确定加入(即删除)。
#include<set>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define pr pair<int,int>
#define mk make_pair
#define fr first
#define sc second
#define IT set<pr>::iterator
using namespace std;
const int maxn=400005;
inline int _read(){
int num=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
return num;
}
struct jz{
int x,y;
jz(int x=0,int y=0):x(x),y(y){}
}S[maxn];
set<pr> A,B,C;
void add(set<pr> &S,pr x){if (S.find(x)==S.end()) S.insert(x);else S.erase(x);}
int n,m,L,R,K,ans[maxn],tg[maxn],tim,top,s[maxn],f[maxn];
pr ed[maxn][2];
int get(int x){if (f[x]==x) return x;return get(f[x]);}
void merge(int x,int y){
x=get(x);y=get(y);if (x==y) return;
if (s[x]<s[y]) swap(x,y);S[++top]=jz(x,y);
s[x]+=s[y];f[y]=x;
}
void Back(int t){
while(top!=t){
int x=S[top].x,y=S[top].y;
f[y]=y;s[x]-=s[y];top--;
}
}
void add(pr h){merge(h.fr,h.sc);}
void Build(){
for (int i=1;i<=n;i++) f[i]=i,s[i]=1;top=0;
for (IT i=C.begin();i!=C.end();i++) add(*i);tim=top;
}
int ask(pr h){return get(h.fr)==get(h.sc);}
int main(){
freopen("exam.in","r",stdin);
freopen("exam.out","w",stdout);
n=_read();m=_read();K=sqrt(2*m*log(m));
for (int j=1;j<=m;j+=K){
L=j;R=min(m,L+K-1);A.clear();B.clear();C.clear();
for (int i=L;i<=R;i++){
tg[i]=_read();int x=_read(),y=_read();
if (x>y) swap(x,y);ed[i][0]=mk(x,y);
x=x%n+1;y=y%n+1;
if (x>y) swap(x,y);ed[i][1]=mk(x,y);
if (tg[i]==1) A.insert(ed[i][0]),A.insert(ed[i][1]);
}
for (int i=1;i<L;i++) if (tg[i]==1){
if (A.find(ed[i][ans[i]])==A.end()) add(C,ed[i][ans[i]]);else add(B,ed[i][ans[i]]);
}
Build();
for (int i=L;i<=R;i++)
if (tg[i]==1) ans[i]=ans[i-1],add(B,ed[i][ans[i]]);else{
Back(tim);for (IT t=B.begin();t!=B.end();t++) add(*t);
ans[i]=ask(ed[i][ans[i-1]]);
}
}
for (int i=1;i<=m;i++) if (tg[i]==2) putchar(ans[i]+'0');
return 0;
}