C137 线段树分治+并查集 P2147 [SDOI2008] 洞穴勘测

视频链接:C137 线段树分治 P2147 [SDOI2008] 洞穴勘测_哔哩哔哩_bilibili

 

 

P2147 [SDOI2008] 洞穴勘测 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

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

#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
const int N=200005;
int n,m,top,ans[N];
int p[N],siz[N]; //并查集
struct node{
  int x,y;
}st[N]; //
struct Q{
  int x,y,b; //b=1是查询,b=0不是
  bool operator<(const Q &a)const{
    return x==a.x?y<a.y:x<a.x;
  }
}q[N]; //操作
vector<Q>tr[N<<2]; //节点
map<Q,int> mp;     //操作信息<<x,y,b>,i>

void insert(int u,int l,int r,int L,int R,Q q){
  if(l>R||r<L) return;
  if(L<=l&&r<=R) return tr[u].push_back(q);
  insert(ls,l,mid,L,R,q);
  insert(rs,mid+1,r,L,R,q);
}
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(i.x,i.y);
   
  if(l==r){
    if(q[l].b) ans[l]=find(q[l].x)==find(q[l].y);
  }
  else solve(ls,l,mid),solve(rs,mid+1,r);
  
  while(top>now){ //撤销合并
    node t=st[top--];
    p[t.x]=t.x;
    siz[t.y]-=siz[t.x];
  }
}
int main(){
  scanf("%d%d",&n,&m); char op[9];
  for(int i=1;i<=n;i++) p[i]=i,siz[i]=1;
  for(int i=1,x,y;i<=m;i++){
    scanf("%s%d%d",op,&x,&y);
    if(x>y) swap(x,y); //保证映射唯一
    if(op[0]=='C'){
      q[i]={x,y,0};
      mp[q[i]]=i;  //记录边q[i]的出现时刻
    }
    else if(op[0]=='D'){
      q[i]={x,y,0};
      insert(1,1,m,mp[q[i]],i,q[i]);//插入q[i]的出现时间
      mp.erase(q[i]); //删除边q[i]
    }
    else q[i]={x,y,1}; //q[i]是查询
  }
  for(auto i:mp) //插入每条边的出现时间
    insert(1,1,m,i.second,m,i.first);
  solve(1,1,m);
  for(int i=1;i<=m;i++)if(q[i].b)puts(ans[i]?"Yes":"No");
}

 

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

#define ls (u<<1)
#define rs (u<<1|1)
#define mid ((l+r)>>1)
#define pii pair<int,int>
const int N=200005;
int n,m,top;
int p[N],siz[N]; //并查集
struct node{
  int x,y;
}e[N],st[N]; //
bool qb[N];  //是否查询
vector<pii>tr[N<<2]; //节点
map<pii,int> mp;     //操作信息<<x,y>,i>

void insert(int u,int l,int r,int L,int R,pii e){
  if(l>R||r<L) return;
  if(L<=l&&r<=R) return tr[u].push_back(e);
  insert(ls,l,mid,L,R,e);
  insert(rs,mid+1,r,L,R,e);
}
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(i.first,i.second);
   
  if(l==r){
    if(qb[l]) puts(find(e[l].x)==find(e[l].y)?"Yes":"No");
  }
  else solve(ls,l,mid),solve(rs,mid+1,r);
  
  while(top>now){ //撤销合并
    node t=st[top--];
    p[t.x]=t.x;
    siz[t.y]-=siz[t.x];
  }
}
int main(){
  scanf("%d%d",&n,&m); char op[9];
  for(int i=1;i<=n;i++) p[i]=i,siz[i]=1;
  for(int i=1,x,y;i<=m;i++){
    scanf("%s%d%d",op,&x,&y);
    if(x>y) swap(x,y); //保证映射唯一
    pii t={x,y};
    e[i]={x,y};
    if(op[0]=='C')mp[t]=i; //记录边的出现时刻
    else if(op[0]=='D'){
      insert(1,1,m,mp[t],i,t); //插入边的出现时间
      mp.erase(t); //删除边
    }
    else qb[i]=1; //i是查询
  }
  for(auto i:mp) //插入每条边的出现时间
    insert(1,1,m,i.second,m,i.first);
  solve(1,1,m);
}

 

posted @ 2024-06-11 18:21  董晓  阅读(67)  评论(0编辑  收藏  举报