loj121 动态图连通性 解题报告

loj121 动态图连通性 传送门

题目描述

这是一道被离线爆艹的模板题。

你要维护一张无向简单图。你被要求加入删除一条边及查询两个点是否连通。

  • 0:加入一条边。保证它不存在。
  • 1:删除一条边。保证它存在。
  • 2:查询两个点是否连通。

输入格式

输入的第一行是两个数 \(N\ M\)\(N \leq 5000,M \leq 500000\)

接下来 \(M \)行,每一行三个数 \(\mathit{op}\), \(x\), \(y\)\(\mathit{op}\) 表示操作编号。

输出格式

对于每一个 \(\mathit{op}=2\) 的询问,输出一行 Y 或 N ,表示两个节点是否连通。

样例 1

输入
200 5
2 123 127
0 123 127
2 123 127
1 127 123
2 123 127
输出
N
Y
N

样例 2

输入
4 10
0 1 2
0 2 3
0 3 1
2 1 4
0 4 3
2 1 4
1 2 3
2 1 4
1 1 3
2 1 4
输出
N
Y
Y
N

数据范围与提示

对于数据点 1,\(N \leq 200,M \leq 200\)
对于数据点 2,\(N=5,M \leq 30\)
对于数据点 3,\(N=10,M \leq 1000\),其中查询的次数\( \geq 900\) 次。
对于数据点 4,\(N=300,M \leq 50000\)
对于数据点 5,\(N=5000,M \leq 200000\),没有操作 1,其中约\( 70 \%\) 是操作 2。
对于数据点 6,\(N=5000,M \leq 200000\),没有操作 1,其中约 \(70 \%\) 是操作 0。
对于数据点 7、8,\(N=100,M \leq 500000\)
对于数据点 9,\(N=5000,M \leq 500000\),图是一棵树,其直径 \(\leq 6\)
对于数据点 10, \(N=5000,M \leq 500000\),图是一棵树,其每个点度数 \(\leq 4\)

P.S. 其实 9 是菊花,10 是单链,而没有放随机树的点...

solution

这个题有点意思。。。。

何止是有点意思啊

连通性可以用并查集维护,但是由于存在删除操作,所以注意不能路径压缩

并查集需要用到启发式合并(就是按秩合并??)

考虑线段树分治。

具体地,假设一条边在时间\(i\)被插入,在时间j被删除,

那么这条边的一个有效时间就是从\(i\)\(j-1\)

我们在线段树的每个节点上开一个vector

通过线段树区间赋值把这个操作添加到这些节点中。

之后从左往右扫线段树就行了,并查集更新的时候开一个栈维护一下变化,

出节点时清除该节点的变化

CODE

#include<bits/stdc++.h>
#define fi first
#define se second
#define pai pair<int,int>
using namespace std;
const int N=5e3+2,M=5e5+2;
int n,m,fa[N],sz[N],st[N],top,ans[M],op,x,y;
pai q[M];
map<pai,int> mp;
map<pai,int> ::iterator it;
vector<pai> V[M*4];
void add(int x,int y,int ti){
    if(x>y) swap(x,y);
    mp[make_pair(x,y)]=ti;
}
void build(int x,int l,int r,int L,int R,pai d){
    if(L<=l&&r<=R){
        V[x].push_back(d); 
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid) build(x<<1,l,mid,L,R,d);
    if(R>mid) build(x<<1|1,mid+1,r,L,R,d);
}
void del(int x,int y,int tm){
    if(x>y) swap(x,y);
    auto p=make_pair(x,y);
    build(1,1,m,mp[p],tm-1,p);
    mp[p]=0;
}
int find(int x){
    if(x==fa[x]) return x;
    return find(fa[x]);
}
void merge(int x,int y){
    x=find(x),y=find(y);
    if(x==y) return;
    if(sz[x]>sz[y]) swap(x,y);
    fa[x]=y;  
    sz[y]+=sz[x];  
    st[++top]=x;
}

void update(int tp){
    while (top>tp){
        int x=st[top--];
        sz[fa[x]]-=sz[x];
        fa[x]=x;
    }
}
void solve(int x,int l,int r){
    int tp1=top;
    for(int i=0;i<(int)V[x].size();i++) {
        int xx=V[x][i].fi,yy=V[x][i].se;
        merge(xx,yy);
    }
    if(l==r){
        if(q[l].fi) {
            int xx=find(q[l].fi),yy=find(q[l].se);
            if(xx==yy) ans[l]=1;
            else ans[l]=0;
        }
        update(tp1); 
        return ;
    }
    int mid=(l+r)>>1;
    solve(x<<1,l,mid);
    solve(x<<1|1,mid+1,r);
    update(tp1);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) {
        fa[i]=i;sz[i]=1;
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&op,&x,&y);
        if(op==0) add(x,y,i);
        else if(op==1) del(x,y,i);
        else q[i]=make_pair(x,y);
    }
    for(it=mp.begin();it!=mp.end();it++){
        if((*it).se){
            auto p=(*it).fi;
            del(p.fi,p.se,m+1);
            mp[p]=0;
        }
    }
    solve(1,1,m);
    for(int i=1;i<=m;i++) {
        if(q[i].fi){
            if(ans[i]) printf("Y\n");
            else printf("N\n");
        }
    }
    return 0;
}

完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★

posted @ 2022-08-15 19:42  _Youngxy  阅读(190)  评论(0)    收藏  举报