[动态维护图连通性] LOJ#122. 【强制在线】动态图连通性

7 篇文章 0 订阅

UPD:已被HACK

学一发黑科技

给边设一个权值,对每一种权值 i i i 维护权值大于等于 i i i 的边构成的最大生成树 T i T_i Ti

加边
一个边刚被加入时的权值设为0,如果它连接两个联通块,就把它设为树边,否则设为非树边

删边

假设要删除边 ( x , y ) (x,y) (x,y),设它的权值为 w w w

如果它是一条非树边,直接删去,否则要找一条边替代它。

显然替代它的边的边权小于等于 w w w

设删掉这条边后形成的两个联通块为 X X X Y Y Y,不妨设 ∣ X ∣ ≤ ∣ Y ∣ |X|\le |Y| XY

那么就是要找一条边权小于等于 w w w 且连接 X X X Y Y Y 的边

暴力枚举所有和 X X X 相连的边就可以了…如果当前枚举到边是 X X X 内部的,把它权值加一,否则结束查找

用LCT维护复杂都是 O ( m log ⁡ 2 n ) O(m\log^2 n) O(mlog2n)

复杂度证明的话

因为每次最多把一个联通块的一半权值加一,而 T i + 1 ⊆ T i T_{i+1}\subseteq T_i Ti+1Ti,所以 T i + 1 T_{i+1} Ti+1 的大小最大为 T i T_{i} Ti 的一半,那么层数就是 log ⁡ n \log n logn

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
#include <assert.h>

using namespace std;

const int N=5010,M=12;

int ttt;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

struct LCT{
  struct node{
    node *ch[2],*f;
    int size,isize,rev;
    node(){ size=1; isize=0; ch[0]=ch[1]=f=0;}
  }a[N];

  inline void Up(node *x){ x->size=(x->ch[0]?x->ch[0]->size:0)+(x->ch[1]?x->ch[1]->size:0)+x->isize+1; }
  
  inline int isl(node *x){ return !x->f || (x->f->ch[0]!=x && x->f->ch[1]!=x); }
  inline int dir(node *x){ return x->f && x->f->ch[1]==x; }

  inline void rot(node *x){
    node *y=x->f,*z=y->f; int wh=dir(x);
    if(!isl(y)) z->ch[dir(y)]=x; x->f=z;
    if(y->ch[wh]=x->ch[wh^1]) y->ch[wh]->f=y;
    (x->ch[wh^1]=y)->f=x; Up(y); Up(x);
  }

  inline void Push(node *x){
    if(!x || !x->rev) return ;
    swap(x->ch[0],x->ch[1]);
    if(x->ch[0])
      x->ch[0]->rev^=1;
    if(x->ch[1])
      x->ch[1]->rev^=1;
    x->rev=0;
  }

  void Pushtop(node *x){ if(!isl(x)) Pushtop(x->f); Push(x); }
  
  inline void splay(node *x){ Pushtop(x); for(;!isl(x);rot(x))if(!isl(x->f)) rot((dir(x)^dir(x->f))?x:x->f); }

  inline void access(node *x){
    for(node *t=0;x;x=x->f){
      splay(x);
      x->isize-=t?t->size:0;
      x->isize+=x->ch[1]?x->ch[1]->size:0;
      x->ch[1]=t; t=x; Up(x);
    }
  }

  inline void reverse(node *x){
    access(x); splay(x); x->rev^=1;
  }
  
  inline int Size(int u){
    node *x=a+u; reverse(x); return x->size;
  }

  inline void link(int u,int v){
    node *x=a+u,*y=a+v;
    reverse(y); reverse(x);
    x->f=y; y->isize+=x->size;
    access(x);
  }

  inline void cut(int u,int v){
    node *x=a+u,*y=a+v;
    reverse(x); access(y); splay(y); y->ch[0]=x->f=0; Up(y);
  }

  inline int linked(int u,int v){
    node *x=a+u,*y=a+v;
    reverse(x); access(y); splay(y);
    while(y->ch[0]) y=y->ch[0];
    return x==y;
  }
};

namespace DG{
  LCT fst[M+2];
  set<int> iT[M+2][N],nT[M+2][N];

  inline void link(int u,int v){
    if(fst[0].linked(u,v)){
      nT[0][u].insert(v); nT[0][v].insert(u);
    }
    else{
      fst[0].link(u,v);
      iT[0][u].insert(v); iT[0][v].insert(u);
    }
  }

  void dfs(int lev,int u,int v,int fa,bool &f){
    for(int i=lev+1;i<M;i++){
      for(int t : iT[i][u])
	if(t!=fa) dfs(lev,t,v,u,f);
    }
    set<int>::iterator i=iT[lev][u].begin();
    while(i!=iT[lev][u].end() && !f){
      int cur=*i;
      iT[lev][u].erase(i++);
      iT[lev][cur].erase(u);
      fst[lev+1].link(u,cur);
      iT[lev+1][u].insert(cur);
      iT[lev+1][cur].insert(u);
      dfs(lev,cur,v,u,f);
    }
    i=nT[lev][u].begin();
    while(i!=nT[lev][u].end() && !f){
      int cur=*i;
      nT[lev][u].erase(i++);
      nT[lev][cur].erase(u);
      if(fst[lev].linked(cur,v)){
	iT[lev][cur].insert(u);
	iT[lev][u].insert(cur);
	for(int j=0;j<=lev;j++)
	  fst[j].link(cur,u);//f=fst[j].linked(cur,u);
	f=1;
      }
      else{
	nT[lev+1][cur].insert(u);
	nT[lev+1][u].insert(cur);
      }
    }
  }

  inline void fix(int lev,int u,int v){
    bool f=0;
    for(int i=lev;~i;i--){
      if(fst[i].Size(u)>fst[i].Size(v)) swap(u,v);
      dfs(i,u,v,0,f);
      if(f) break;
    }
  }

  inline void cut(int u,int v){
    int lev;
    for(lev=0;lev<M;lev++){
      if(nT[lev][u].count(v)){
	nT[lev][u].erase(v);
	nT[lev][v].erase(u);
	return ;
      }
      else if(iT[lev][u].count(v)){
	iT[lev][u].erase(v);
	iT[lev][v].erase(u);
	break;
      }
    }
    for(int i=0,c;i<=lev;i++)
      fst[i].cut(u,v);//c=fst[i].linked(u,v);
    fix(lev,u,v);
  }

  inline bool linked(int u,int v){
    return fst[0].linked(u,v);
  }
}

int n,m,lst;

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  read(n); read(m);
  while(m--){
    int opt,u,v; read(opt); read(u); read(v);
    u^=lst; v^=lst;
    if(opt==0)
      DG::link(u,v);
    else if(opt==1)
      DG::cut(u,v);
    else{
      if(DG::linked(u,v)) lst=u;
      else lst=v;
      puts(lst==u?"Y":"N");
    }
  }
  return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值