QTREE5 - Query on a tree V——LCT

QTREE5 - Query on a tree V 

动态点分治和动态边分治用Qtree4的做法即可。

LCT:

换根后,求子树最浅的白点深度。

但是也可以不换根。类似平常换根的往上g,往下f的拼凑

考虑深度的pushup必须考虑原树结构的联系,而ch[0],ch[1]又不是直接的前驱后继,每次pushup还要找前驱后继答案,还不如直接记下来。

故,节点里维护:

1.sz,大小

2.color节点颜色

3.set每个虚儿子贡献的最浅深度

4.lmn,rmn,当前x的splay子树最浅点和最深点的,展开成的实链的范围内的答案。

也就是:

pushup:

后面的就是跨过x的拼凑。top是虚儿子set的最小值

 

access:

虚实儿子转化

 

修改,access,splay,t[x].co^1

查询:access,splay, cout<<t[x].rmn

 

代码:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=100000+5;
const int inf=0x3f3f3f3f;
int n,q;
struct node{
    int ch[2],fa;
    int lmn,rmn;
    int co;//1:white 0:black
    int sz;
    multiset<int>s;
    multiset<int>::iterator it;
    int top(){
        if(s.size()) return *s.begin();
        return inf;
    }
    void ins(int c){
        s.insert(c);
    }
    void dele(int c){
        it=s.lower_bound(c);
        if(it!=s.end()) s.erase(it);
    }
    void op(){
        cout<<" ch[0] "<<ch[0]<<" ch[1] "<<ch[1]<<" fa "<<fa<<endl;
        cout<<" co "<<co<<" sz "<<sz<<endl;
        cout<<" lmn "<<lmn<<"  rmn "<<rmn<<endl;
        cout<<" s.size() "<<s.size()<<endl;
    }
}t[N];
int nrt(int x){
    return t[t[x].fa].ch[0]==x||t[t[x].fa].ch[1]==x;
}
void pushup(int x){
    if(!x) return;
    t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+1;
    t[x].lmn=min(t[t[x].ch[0]].lmn,t[t[x].ch[0]].sz+min(t[x].co?0:inf,min(t[x].top(),t[t[x].ch[1]].lmn+1)));
    t[x].rmn=min(t[t[x].ch[1]].rmn,t[t[x].ch[1]].sz+min(t[x].co?0:inf,min(t[x].top(),t[t[x].ch[0]].rmn+1)));
}
void rotate(int x){
    int y=t[x].fa,d=t[y].ch[1]==x;
    t[t[y].ch[d]=t[x].ch[!d]].fa=y;
    if(nrt(y)) t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x;
    else t[x].fa=t[y].fa;
    t[t[x].ch[!d]=y].fa=x;
    pushup(y);
}
void splay(int x){
    int y,z;
    while(nrt(x)){
        y=t[x].fa,z=t[y].fa;
        if(nrt(y)){
            rotate((t[y].ch[0]==x)==(t[z].ch[0]==y)?y:x);
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x){
//    cout<<" access "<<x<<endl;
    for(reg y=0;x;y=x,x=t[x].fa){
        splay(x);
        if(y) t[x].dele(t[y].lmn+1);
        if(t[x].ch[1]) t[x].ins(t[t[x].ch[1]].lmn+1);
        t[x].ch[1]=y;
        pushup(x);
      //  cout<<" x "<<x<<" y "<<y<<endl;
     //   t[x].op();
    }
}
struct edge{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
void dfs(int x,int fa){
    t[x].sz=1;
    t[x].lmn=inf;t[x].rmn=inf;
    t[x].co=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        t[y].fa=x;
        dfs(y,x);
    }
}
int main(){
    rd(n);
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    rd(q);
    int op;
    t[0].lmn=inf,t[0].sz=0;
    t[0].rmn=inf;t[0].co=233;
    while(q--){
        rd(op);rd(x);
        if(!op){
            access(x);splay(x);
            t[x].co^=1;
            pushup(x);
        //    t[x].op();
        }else{
            access(x);splay(x);
        //    t[x].op();
            printf("%d\n",t[x].rmn<=n?t[x].rmn:-1);
        }
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/3/14 21:49:23
*/

 

见着拆招,pushup不好处理,就直接记录lmn和rmn,lmn用于access更新set,rmn用于查询答案。

如果要换根,还要考虑swap(lmn,rmn)

 

转载于:https://www.cnblogs.com/Miracevin/p/10534759.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值