C134 线段树分治+并查集 P5227 [AHOI2013] 连通图

视频链接:C134 线段树分治 P5227 [AHOI2013] 连通图_哔哩哔哩_bilibili

 

 

P5227 [AHOI2013] 连通图 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

// 线段树分治 O(nlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define int long long
#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
const int N=100005;
int n,m,k;
int p[N],high[N],siz[N],top;
pair<int,int>e[N<<1]; //存边
vector<int>del[N<<1]; //边i消失的时刻
vector<int>tr[N<<2];  //线段树节点
struct node{
  int x,y,hy;
}st[N<<1]; //用栈记录边的端点的合并信息

void insert(int u,int l,int r,int L,int R,int i){
  if(L<=l&&r<=R) return tr[u].push_back(i);
  if(L<=mid) insert(ls,l,mid,L,R,i);
  if(R>mid) insert(rs,mid+1,r,L,R,i);
}
int find(int x){ //查找根
  return p[x]==x?x:find(p[x]);
}
void merge(int x,int y){ //合并集合
  x=find(x),y=find(y);
  if(x==y) return;
  if(high[x]>high[y]) swap(x,y);
  st[++top]={x,y,high[y]};
  p[x]=y; //x连向y
  high[y]+=(high[y]==high[x]);
  siz[y]+=siz[x]; //累加连通块的大小
}
void solve(int u,int l,int r){
  int now=top;
  //合并当前区间出现的边
  for(auto i:tr[u])
    merge(e[i].first,e[i].second);
  //判断去掉r时刻集合中的边后图是否联通
  if(l==r) siz[find(1)]==n?
    puts("Connected"):puts("Disconnected");
  else solve(ls,l,mid),solve(rs,mid+1,r);
  
  while(top>now){ //撤销合并
    node s=st[top--];
    p[s.x]=s.x;
    high[s.y]=s.hy;
    siz[s.y]-=siz[s.x];
  }
}
signed main(){
  scanf("%lld%lld",&n,&m);
  for(int i=1;i<=n;++i) p[i]=i,siz[i]=1;
  for(int i=1,x,y;i<=m;++i){
    scanf("%lld%lld",&x,&y);
    e[i].first=x,e[i].second=y;
    del[i].push_back(0);
  }
  scanf("%lld",&k);
  for(int i=1,c,j;i<=k;++i){ //k个时刻
    scanf("%lld",&c); //删c条边
    while(c--){
      scanf("%lld",&j);
      del[j].push_back(i); //边j消失的时刻
    }
  }
  for(int i=1;i<=m;++i){
    del[i].push_back(k+1);
    for(int j=0;j<del[i].size()-1;++j)
      if(del[i][j+1]-del[i][j]>1)
        insert(1,1,k,del[i][j]+1,del[i][j+1]-1,i);
        //边i出现的时间[del[i][j]+1,del[i][j+1]-1]
  }
  solve(1,1,k);
}

 

// 线段树分治 O(nlognlogn)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define int long long
#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
const int N=100005;
int n,m,k;
int p[N],siz[N],top;
pair<int,int>e[N<<1]; //存边
vector<int>del[N<<1]; //边i消失的时刻
vector<int>tr[N<<2];  //线段树节点
struct node{
  int x,y;
}st[N<<1]; //用栈记录边的端点的合并信息

void insert(int u,int l,int r,int L,int R,int i){
  if(L<=l&&r<=R) return tr[u].push_back(i);
  if(L<=mid) insert(ls,l,mid,L,R,i);
  if(R>mid) insert(rs,mid+1,r,L,R,i);
}
int find(int x){ //查找根
  return p[x]==x?x:find(p[x]);
}
void merge(int x,int y){ //合并集合
  x=find(x),y=find(y);
  if(x==y) return;
  if(siz[x]>siz[y]) swap(x,y);
  st[++top]={x,y};
  p[x]=y;
  siz[y]+=siz[x];
}
void solve(int u,int l,int r){
  int now=top;
  for(auto i:tr[u])
    merge(e[i].first,e[i].second);

  if(l==r) siz[find(1)]==n?
    puts("Connected"):puts("Disconnected");
  else solve(ls,l,mid),solve(rs,mid+1,r);
  
  while(top>now){ //撤销合并
    node s=st[top--];
    p[s.x]=s.x;
    siz[s.y]-=siz[s.x];
  }
}
signed main(){
  scanf("%lld%lld",&n,&m);
  for(int i=1;i<=n;++i) p[i]=i,siz[i]=1;
  for(int i=1,x,y;i<=m;++i){
    scanf("%lld%lld",&x,&y);
    e[i].first=x,e[i].second=y;
    del[i].push_back(0);
  }
  scanf("%lld",&k);
  for(int i=1,c,j;i<=k;++i){ //k个时刻
    scanf("%lld",&c); //删c条边
    while(c--){
      scanf("%lld",&j);
      del[j].push_back(i); //边j消失的时刻
    }
  }
  for(int i=1;i<=m;++i){
    del[i].push_back(k+1);
    for(int j=0;j<del[i].size()-1;++j)
      if(del[i][j+1]-del[i][j]>1)
        insert(1,1,k,del[i][j]+1,del[i][j+1]-1,i);
        //边i出现的时间[del[i][j]+1,del[i][j+1]-1]
  }
  solve(1,1,k);
}

 

posted @ 2024-06-05 10:51  董晓  阅读(110)  评论(0编辑  收藏  举报