分治算法
线段树分治
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");
}
}

浙公网安备 33010602011771号