[线段树] CodeChef Count on a Treap

要求两点距离,只需做到能求两点 lca 和任意一点的深度即可。
Treap其实是一棵笛卡尔树,构造过程就是对于 key 递增的序列,每次选 w 最大的作为根,然后两边分为左右子树递归。
那么x,y的 lca 实际上就是 [x,y] w 最大的,比较显然。
然后就是怎么求深度,考虑一个节点 x 的父亲,它一定是序列中离 x 最近的且 w 大于 w(x) 的位置。然后不断的往上找到根。
自己画一下这个过程可以发现,这个就等价于从 x 开始向左走的上升序列长度加上向右走的上升序列长度。这里的上升序列指的是每次取最近的比当前大的,到最大值为止。
所以只要线段树维护上升序列就好了,每次 maintain 求上升序列长度需要多一个 log 二分下去。

感觉代码思路蛮清楚的,1A了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define Max(p) a[p->pos_max]
#define lenL(p) p->lenL
#define lenR(p) p->lenR
using namespace std;
typedef unsigned long long uLL;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline uLL getuLL(){
    char ch=gc(); uLL res=0;
    while(!('0'<=ch&&ch<='9')) ch=gc();
    while('0'<=ch&&ch<='9') res=(res<<3)+(res<<1)+ch-'0', ch=gc();
    return res;
}
const int maxn=400010;
uLL b[maxn],a[maxn];
struct node{
    node* ch[2];
    int L,R,pos_max,lenL,lenR;
    node(int t1=0,int t2=0,int t3=0,int t4=0,int t5=0,node* t6=0){L=t1;R=t2;pos_max=t3;lenL=t4;lenR=t5;ch[0]=ch[1]=t6;}
    int findL(int x){
        if(!pos_max) return 0;
        if(L==R) return x<a[pos_max];
        if(Max(ch[0])<x) return ch[1]->findL(x);
                    else return ch[0]->findL(x)+lenL-lenL(ch[0]); 
    }
    int findR(int x){
        if(!pos_max) return 0;
        if(L==R) return x<a[pos_max];
        if(Max(ch[1])<x) return ch[0]->findR(x);
                    else return ch[1]->findR(x)+lenR-lenR(ch[1]); 
    }
    void maintain(){
        pos_max=Max(ch[0])>Max(ch[1])?ch[0]->pos_max:ch[1]->pos_max;
        lenL=lenL(ch[0])+ch[1]->findL(Max(ch[0]));
        lenR=lenR(ch[1])+ch[0]->findR(Max(ch[1]));
    }
} *root, nil, *null=&nil;
typedef node* P_node;
P_node Build(int L,int R){
    P_node p=new node(L,R,0,0,0,null);
    if(L==R) return p;
    int mid=(L+R)>>1;
    p->ch[0]=Build(L,mid); p->ch[1]=Build(mid+1,R);
    return p;
}
void Updata(P_node p,int pos,int k,uLL val=0){
    if(pos<p->L||p->R<pos) return;
    if(p->L==p->R){
        if(k==1) a[p->pos_max=p->L]=val, p->lenL=p->lenR=1;
            else a[p->L]=0, p->pos_max=p->lenL=p->lenR=0; 
        return;
    }
    Updata(p->ch[0],pos,k,val); Updata(p->ch[1],pos,k,val);
    p->maintain();
}
int ans; uLL now_max;
void QueryL(P_node p,int L,int R){
    if(R<p->L||p->R<L) return;
    if(L<=p->L&&p->R<=R){
        int t=p->findL(now_max); ans+=t; if(t) now_max=Max(p); 
        return;
    }
    QueryL(p->ch[0],L,R); QueryL(p->ch[1],L,R);
}
void QueryR(P_node p,int L,int R){
    if(R<p->L||p->R<L) return;
    if(L<=p->L&&p->R<=R){
        int t=p->findR(now_max); ans+=t; if(t) now_max=Max(p); 
        return;
    }
    QueryR(p->ch[1],L,R); QueryR(p->ch[0],L,R);
}
int getDep(int x){
    ans=0;
    now_max=a[x]; QueryL(root,x+1,b[0]);
    now_max=a[x]; QueryR(root,1,x-1);
    return ans;
}
int Query_max(P_node p,int L,int R){
    if(R<p->L||p->R<L) return 0;
    if(L<=p->L&&p->R<=R) return p->pos_max;
    int t1=Query_max(p->ch[0],L,R),t2=Query_max(p->ch[1],L,R);  
    return a[t1]>a[t2]?t1:t2;
}
struct data{ uLL k,x,y; } q[maxn];
int ID(uLL x){ return lower_bound(b+1,b+1+b[0],x)-b; } 
int Q;
int main(){
    freopen("cot5.in","r",stdin);
    freopen("cot5.out","w",stdout);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++){
        q[i].k=getuLL(); q[i].x=getuLL(); b[++b[0]]=q[i].x;
        if(q[i].k!=1) q[i].y=getuLL();
    }
    sort(b+1,b+1+b[0]); b[0]=unique(b+1,b+1+b[0])-(b+1);
    root=Build(1,b[0]);
    for(int i=1;i<=Q;i++){
        if(q[i].k==0) Updata(root,ID(q[i].x),1,q[i].y); else
        if(q[i].k==1) Updata(root,ID(q[i].x),-1); else{
            int x=ID(q[i].x),y=ID(q[i].y); if(x>y) swap(x,y);
            int _lca=Query_max(root,x,y);
            printf("%d\n",getDep(x)+getDep(y)-getDep(_lca)*2);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值