题意
n个点的图,一开始是空图,两种操作,一种是增减 ( u , v ) (u,v) (u,v),若之前无边,则加上该边,若之前有边,则去掉该边,另一种是提问两个点,看是否联通,强制在线。
题解
先考虑不强制在线的解法
定期重构,每次求解一个块内的答案
首先,将块之前的边且最后留下来的边分为两种
一是在块内没有出现的边
二是在块内出现的边
对于第一种边,直接建立即可,而且后面也不会被删去
在建完第一种边之后,紧接着建立第二种边,他们在后面更改
遍历块内的边
若为加边,直接加
若为减边,则回滚到增加该边的操作,跳过该边,将回滚时删去的边再加入,就完成了删边的操作
根据建图的特殊顺序,第二种最多有
m
\sqrt m
m 种,所以每次回滚为
O
(
m
)
O(\sqrt m)
O(m)
并查集的复杂度为
l
o
g
(
n
)
log(n)
log(n)
所以总复杂度为
m
m
l
o
g
(
n
)
m\sqrt m ~log(n)
mm log(n)
考虑强制在线,题目中的强制在线的方法,只有两种情况
(
u
,
v
)
或
(
u
+
1
,
v
+
1
)
(u,v)或(u+1,v+1)
(u,v)或(u+1,v+1)
所以,这两种边都存下
有影响的只有块前的边建图的时候,无非是第二类的边多了一点,发现对复杂度没有影响
实际测试,发现,块的大小,
m
\sqrt m
m不是最优,
20
m
20\sqrt m
20m左右是最优的
不难理解,每个块求解一次,
O
(
n
)
O(n)
O(n)的遍历是不可少的,但在增减边的时候,减边期望只有不到1/3,而且回滚时复杂度跑不慢,所以可以增加块的大小,增加回滚时间,减少块的个数
代码
#include<bits/stdc++.h>
#define N 400010
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi 3.141592653589793
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef pair<int,LL> pp;
int n,m,cnt,ans,tot;
unordered_map<LL,int> id;
int op[N],d[N],fa[N],rk[N],lg[N],hs[N];
struct node{
int u,v; LL h;
void bb(){if (u>v) swap(u,v);h=(LL) u<<30|v; }
}e[N],g[N];
int getfa(int x){ return fa[x]==x?x:getfa(fa[x]); }
void uni(int x,int y,int z){
x=getfa(x),y=getfa(y);
if (rk[x]<=rk[y]){
fa[x]=y; lg[++tot]=x; hs[tot]=z;
if (rk[x]==rk[y]) rk[y]++;
}else{
fa[y]=x; lg[++tot]=y; hs[tot]=z;
}
}
void del(int x){
int i;
for(i=tot;hs[i]!=x;i--) fa[lg[i]]=lg[i]; fa[lg[i]]=lg[i];
int u=tot; tot=i-1;
for(i++;i<=u;i++) uni(g[hs[i]].u,g[hs[i]].v,hs[i]);
}
void solve(int be,int en){
for(int i=1;i<=n;i++) fa[i]=i,rk[i]=0; tot=0;
vector<int> v(cnt+3),vv(cnt+3);
for(int i=1;i<be;i++) if (op[i]==1) v[d[i]]^=1;
for(int i=be;i<=en;i++) if (op[i]==1) vv[d[i]]=vv[d[i+m]]=1;
for(int i=1;i<=cnt;i++) if (v[i]&&!vv[i]) uni(g[i].u,g[i].v,i);
for(int i=1;i<=cnt;i++) if (v[i]&&vv[i]) uni(g[i].u,g[i].v,i);
for(int i=be;i<=en;i++){
if (ans) e[i]=e[i+m],d[i]=d[i+m];
if (op[i]==1){
v[d[i]]^=1;
if (v[d[i]]) uni(e[i].u,e[i].v,d[i]); else del(d[i]);
}else{
int x=getfa(e[i].u),y=getfa(e[i].v);
if (x!=y) putchar('0'),ans=0;else putchar('1'),ans=1;
}
}
}
int main(int argc, char const *argv[])
{
scc(n,m);
for(int i=1;i<=m;i++){
sc(op[i]); scc(e[i].u,e[i].v); e[i].bb();
op[i+m]=op[i]; e[i+m]={e[i].u%n+1,e[i].v%n+1}; e[i+m].bb();
}
for(int i=1,tot=m*2;i<=tot;i++){
LL t=e[i].h;
if (!id[t]) id[t]=++cnt,g[cnt]=e[i];
d[i]=id[t];
}
int B=sqrt(m)*30; B=min(B,m);
for(int i=1;i<=m;i+=B) solve(i,min(i+B-1,m));
return 0;
}