uoj#295. 【ZJOI2017】線段樹(樹上倍增)

先放代碼,日後更。

============================2018.3.21UPD============================
題面在這裏

做法

首先需要了解zkw線段樹的操作過程。(不懂的百度一下)
大概就是從兩個葉節點開始,維護兩個指針,一個指向l 左邊一位,一個指向r 右邊一位,不停向上跳。然後模仿這個過程同樣在這個廣義線段樹上操作,畫一下圖可以發現,假設l1 所在葉節點ur+1 所在葉節點v ,一個線段[l,r] 會分成的若干個小線段恰好是ulca(u,v) 鏈上所有左兒子的右兄弟,以及vlca(u,v) 鏈上所有右兒子的左兄弟。
一定要畫圖!!!多觀察!!!
發現這一點後就很容易了。將一條鏈按lca 分成兩半,然後大力分類討論,樹上倍增處理一下,細節超多。比如,l=1r=n 的情況需要特殊處理,還有u 的位置比較特殊的時候也需要判一下。
建議自己推推看qwq。(我當時都是自己想的qwq,感覺想出一道zjoi題自己萌!萌!噠!

代碼

/*
*   zkw線段樹的科技;
*   一堆細節;
*   考試一定要對拍!不對拍會死的!
*/
#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x); i<=(y); i++)
#define per(i,x,y) for (int i=(x); i>=(y); i--)
#define N 400010
#define ll long long
using namespace std;
ll read(){
    char ch=getchar(); ll x=0; int op=1;
    for (; !isdigit(ch); ch=getchar()) if (ch=='-') op=-1;
    for (; isdigit(ch); ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*op;
}
int n,m,tot,rt,now,clk,a[N],f[N][20],pos[N],in[N],out[N];
ll g[N],h[N],f1s[N],f2s[N],gs[N],hs[N],ans,dep[N];
struct seg{
    int l,r,ls,rs,mid;
    seg(){ l=r=ls=rs=mid=0; }
}tr[N];
void build(int &o,int l,int r,int d,int fa,bool fl){
    o=++tot; in[o]=++clk; tr[o].l=l; tr[o].r=r; dep[o]=d; f[o][0]=fa;
    if (!fl){//左兒子
        g[o]=g[fa]+d; gs[o]=gs[fa]+1;
        h[o]=h[fa]; hs[o]=hs[fa];
        f1s[o]=f1s[fa]+d-1; f2s[o]=f2s[fa];
    } else{
        g[o]=g[fa]; gs[o]=gs[fa];
        h[o]=h[fa]+d; hs[o]=hs[fa]+1;
        f1s[o]=f1s[fa]; f2s[o]=f2s[fa]+d-1;
    }
    if (l==r){ pos[l]=o; out[o]=++clk; return; } int k=a[now]; tr[o].mid=k;
    if (l<k) now++; build(tr[o].ls,l,k,d+1,o,0);
    if (k+1<r) now++; build(tr[o].rs,k+1,r,d+1,o,1);
    out[o]=++clk;
}
void print_tree(int u){
    printf("id: %d, l=%d, r=%d\n",u,tr[u].l,tr[u].r);
    if (tr[u].ls) print_tree(tr[u].ls);
    if (tr[u].rs) print_tree(tr[u].rs);
}
int lca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    int tmp=dep[x]-dep[y];
    per (i,18,0) if (tmp>>i&1) x=f[x][i];
    if (x==y) return x;
    per (i,18,0) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
bool isanc(int x,int y){ return in[x]<=in[y] && out[x]>=out[y]; }
int main(){
    /*freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);*/
    n=read();
    rep (i,1,n-1) a[i]=read();
    m=read();
    now=1; build(rt,1,n,0,0,1);
    rep (j,1,18) rep (i,1,tot) f[i][j]=f[f[i][j-1]][j-1];
    while (m--){
        int u=read(),l=read(),r=read(),x,y,z,w,v,tmp;
        ans=0;
        if (l==1 && r==n){ printf("%d\n",dep[u]); continue; }
        x=pos[l-1]; y=pos[r+1]; tmp=lca(x,y); z=tr[tmp].ls; if (l==1 || r==1) z=1;
        if (l!=1){
            w=lca(x,u);
            if (isanc(w,z)){
                ll sum=gs[x]-gs[z];
                ans+=g[x]-g[z]+sum*dep[u]-2ll*sum*dep[w];
                if (w==z && isanc(tr[w].rs,u)) ans-=2;
            } else{
                ll sum=gs[x]-gs[w];
                ans+=g[x]-g[w]+sum*dep[u]-2ll*sum*dep[w];
                sum=gs[w]-gs[z];
                ans+=g[w]-g[z]+sum*dep[u]-2ll*(f1s[w]-f1s[z]);
                if (isanc(tr[w].rs,u)) ans-=2;
            }
        }
        z=tr[tmp].rs; if (l==1 || r==1) z=1;
        if (r!=n){
            w=lca(y,u);
            if (isanc(w,z)){
                ll sum=hs[y]-hs[z];
                ans+=h[y]-h[z]+sum*dep[u]-2ll*sum*dep[w];
                if (w==z && isanc(tr[w].ls,u)) ans-=2;
            } else{
                ll sum=hs[y]-hs[w];
                ans+=h[y]-h[w]+sum*dep[u]-2ll*sum*dep[w];
                sum=hs[w]-hs[z];
                ans+=h[w]-h[z]+sum*dep[u]-2ll*(f2s[w]-f2s[z]);
                if (isanc(tr[w].ls,u)) ans-=2;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章