[BZOJ3510][启发式合并][LCT维护子树信息]首都

题意略…(今天比较懒)

由于只有建造公路没有破环公路,所以可以用启发式合并保持操作数为log,暴力拆开小的联通块,用LCT插到大的联通块内,然后用LCT维护子树信息,具体看这里

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 300010

using namespace std;

int n,m,rt[N],g[N],Ans,x,y,tp;
char op;
int fa[N],ch[N][2],sz[N],isz[N],rev[N],sta[N],tot[N],G[N],cnt;
struct edge{
  int t,nx;
}E[N<<3];

inline int isr(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline int isl(int x){return ch[fa[x]][1]==x;}

int fifa(int x){return rt[x]==x?x:(rt[x]=fifa(rt[x]));}

inline void upd(int x){
  if(!x) return;
  sz[x]=1+isz[x];
  if(ch[x][0]) sz[x]+=sz[ch[x][0]];
  if(ch[x][1]) sz[x]+=sz[ch[x][1]];
}

inline void pushdown(int x){
  if(!(x&&rev[x])) return;
  swap(ch[x][0],ch[x][1]);
  if(ch[x][0]) rev[ch[x][0]]^=1;
  if(ch[x][1]) rev[ch[x][1]]^=1;
  rev[x]=0;
}

inline void rot(int x){
  int y=fa[x],z=fa[y],lor=isl(x);
  if(!isr(y)) ch[z][ch[z][1]==y]=x; fa[x]=z;
  if(ch[y][lor]=ch[x][lor^1]) fa[ch[y][lor]]=y;
  fa[ch[x][lor^1]=y]=x; upd(y); upd(x);
}

inline void splay(int x){
  sta[tp=1]=x;
  for(int i=x;!isr(i);i=fa[i]) sta[++tp]=fa[i];
  for(;tp;--tp) pushdown(sta[tp]);
  for(;!isr(x);rot(x)) if(!isr(fa[x])) rot(isl(x)^isl(fa[x])?x:fa[x]);
}

inline void access(int x){
  int t=0;
  for(;x;x=fa[x]){
    splay(x);
    isz[x]+=sz[ch[x][1]]-sz[t];
    ch[x][1]=t;
    t=x;
    upd(x);
  }
}

inline void reverse(int x){access(x); splay(x); rev[x]^=1;}
inline void link(int x,int y){
  reverse(x);
  reverse(y);
  fa[x]=y;
  isz[y]+=sz[x];
  upd(y);
  reverse(g[fifa(y)]);
  access(x);
  splay(g[fifa(y)]);
  int S=sz[g[fifa(y)]],t=ch[g[fifa(y)]][1];pushdown(t);
  while(ch[t][0]) pushdown(t=ch[t][0]);
  access(t);
  if((isz[t]+1)*2>S||((isz[t]+1)*2==S&&t<g[fifa(y)])){
    Ans^=g[fifa(y)]^t;
    g[fifa(y)]=t;
  }
}

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

inline void InserT(int x,int y){
  E[++cnt].t=y;E[cnt].nx=G[x];G[x]=cnt;
  E[++cnt].t=x;E[cnt].nx=G[y];G[y]=cnt;
}

void dfs(int x,int f){
  ch[x][0]=ch[x][1]=fa[x]=isz[x]=rev[x]=0;
  sz[x]=1;
  link(x,f);
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f) dfs(E[i].t,x);
}

inline void merge(int x,int y){
  tot[fifa(y)]+=tot[fifa(x)];
  Ans^=g[fifa(x)];
  rt[fifa(x)]=fifa(y);
  dfs(x,y); InserT(x,y);
}

int main(){
  reaD(n); reaD(m);
  for(int i=1;i<=n;i++) rt[i]=i,g[i]=i,Ans^=i,tot[i]=1;
  for(int i=1;i<=m;i++){
    while((op=getchar())!='A'&&op!='Q'&&op!='X');
    if(op=='X') printf("%d\n",Ans);
    else if(op=='Q') reaD(x),printf("%d\n",g[fifa(x)]);
    else{
      reaD(x); reaD(y);
      if(tot[fifa(x)]>tot[fifa(y)]) swap(x,y);
      merge(x,y);
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值