分治算法

线段树分治

T2:

线段树分治的是询问,把询问离线,然后线段树的每一个叶子节点为一个询问

把删边转化为加边,记录每一条边在哪些询问区间中加入进来过,例如 边x在 \(a_1...a_2,a_3...a_4,...\) 这些区间中加过

那我们依次将这些区间拆到线段树上,在线段树上的点加入当前的边

然后按照前序遍历遍历一遍线段树,将当前节点存储的边加入并查集,再回溯时用可撤销并查集撤销这些点的贡献即可

可撤销并查集

不能路径压缩,只能按秩合并

每次合并改变了 \(x\)\(fa\)\(y\)\(siz\) ,存储到栈里即可

struct delopt{
    int x,y,siz;
};
vector<delopt>del;
void init(){
    for(int i=1;i<=n;i++)  fa[i]=i,siz[i]=1;
}
int find(int x){
    if(fa[x]==x)  return x;
    return find(fa[x]);
}
void merge(int x,int y){
    x=find(x),y=find(y);
    if(siz[x]>siz[y])  swap(x,y);
    del.push_back({x,y,siz[y]});
    if(x==y)  return;//要在这里写判重
    fa[x]=y;
    // printf("merge %d %d %d %d\n",x,y,siz[x],siz[y]);
    siz[y]+=siz[x];
}
void delate(){
    delopt i=del.back();
    fa[i.x]=i.x;
    siz[i.y]=i.siz;
    del.pop_back();
}
点击完整查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
struct edge{
    int x,y;
}e[N];
struct delopt{
    int x,y,siz;
};
struct para{
    int l,r;
};
int n,m,k;
int fa[N],siz[N],ans[N];
vector<delopt>del;
vector<para>s[N];
vector<int>tr[N*4];
void init(){
    for(int i=1;i<=n;i++)  fa[i]=i,siz[i]=1;
}
int find(int x){
    if(fa[x]==x)  return x;
    return find(fa[x]);
}
void merge(int x,int y){
    x=find(x),y=find(y);
    if(siz[x]>siz[y])  swap(x,y);
    del.push_back({x,y,siz[y]});
    if(x==y)  return;//要在这里写判重
    fa[x]=y;
    // printf("merge %d %d %d %d\n",x,y,siz[x],siz[y]);
    siz[y]+=siz[x];
}
void delate(){
    delopt i=del.back();
    fa[i.x]=i.x;
    siz[i.y]=i.siz;
    del.pop_back();
}
void add(int k,int l,int r,int x,int y,int c){
    if(x<=l&&r<=y){
        tr[k].push_back(c);
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)  add(k*2,l,mid,x,y,c);
    if(y>mid)  add(k*2+1,mid+1,r,x,y,c);
    return;
}
void visit(int k,int l,int r){
    int lst=1;
    for(int i:tr[k])  merge(e[i].x,lst=e[i].y);
    // printf("k=%d %d %d\n",k,l,r);
    // for(int i:tr[k])  printf("%d ",i);
    // printf("\n");
    if(l==r){
        // for(int i=1;i<=n;i++){
        //     printf("i=%d %d %d\n",i,find(i),siz[find(i)]);
        // }
        if(siz[find(lst)]==n)  ans[l]=1;
        else  ans[l]=0;
    }
    else{
        int mid=(l+r)>>1;
        visit(k*2,l,mid);
        visit(k*2+1,mid+1,r);
    }
    for(int i:tr[k])  delate();
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        e[i]={u,v};
    }
    scanf("%d",&k);
    for(int i=1;i<=m;i++){
        s[i].push_back({1,k});
    }
    for(int i=1;i<=k;i++){
        int c;
        scanf("%d",&c);
        for(int j=1;j<=c;j++){
            int e;
            scanf("%d",&e);
            para g=s[e].back();
            s[e].pop_back();
            int l=g.l,r=g.r;
            if(l<i)  s[e].push_back({l,i-1});
            if(i<r)  s[e].push_back({i+1,r});
        }
    }
    init();
    for(int i=1;i<=m;i++){//接下来进行区间加入线段树
        for(auto j:s[i]){
            // printf("%d=%d %d\n",i,j.l,j.r);
            add(1,1,k,j.l,j.r,i);
        }
    }
    visit(1,1,k);
    for(int i=1;i<=k;i++){
        if(ans[i])  printf("Connected\n");
        else  printf("Disconnected\n");
    }
}
posted @ 2025-07-25 09:03  daydreamer_zcxnb  阅读(9)  评论(0)    收藏  举报