CSUST 遞增陣列2 題解(思維+分段考慮)
阿新 • • 發佈:2021-09-15
一定要保護自己的夢想,即使犧牲一切。
序上做一個主席樹的操作,然後就可以查詢子樹內的點的情況了。
我們可以思考怎麼做呢。
首先我們需要進行一些分類討論:
我們先思考一下如果所有關鍵點都在 \(p\) 的子樹內,
那顯然是所有關鍵點的 \(Lca\) 到 \(p\) 距離。
如果所有關鍵點一些在 \(p\) 的子樹裡,一些在子樹外,則答案顯然為 \(0\)。
那我們只需要接著討論一下所有關鍵點在都在子樹外的情況即可。
我們知道一個點一定會沿著祖先往下走,然後在往一個子樹進入。
如果關鍵點全都是在祖先的一個子樹內,那答案一定是這些關鍵點的 \(Lca\) 和 \(p\) 的距離。
否則這個答案一定是到祖先的鏈上的某個點,這個點滿足是這個子樹裡沒有關鍵點且是最淺的點。
區間 \(Lca\) 可以使用線段樹解決,然後我們在 \(dfn\)
// code by fhq_treap #include<bits/stdc++.h> #define ll int #define N 300005 inline ll read(){ char C=getchar(); ll A=0 , F=1; while(('0' > C || C > '9') && (C != '-')) C=getchar(); if(C == '-') F=-1 , C=getchar(); while('0' <= C && C <= '9') A=(A << 1)+(A << 3)+(C - 48) , C=getchar(); return A*F; } template <typename T> void write(T x) { if(x < 0) { putchar('-'); x = -x; } if(x > 9) write(x/10); putchar(x % 10 + '0'); return; } ll n,q; ll head[N],cnt; struct P{ int to,next,w; }e[N << 1]; inline void add(int x,int y,int w){ e[++cnt].to = y,e[cnt].next = head[x],e[cnt].w = w,head[x] = cnt; } //tree ll dfn[N],s[N],inv[N]; ll dfncnt; int fa[N][30],dep[N],end[N]; inline void dfs(int x,int f){ end[x] = dfn[x] = ++dfncnt; dep[x] = dep[f] + 1; inv[dfncnt] = x; fa[x][0] = f; for(int i = 1;i < 30;i ++) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for(int i = head[x];i;i = e[i].next){ int v = e[i].to; if(v == f)continue; s[v] = s[x] + e[i].w; dfs(v,x); end[x] = std::max(end[x],end[v]); } } inline ll lca(ll x,ll y){ if(dep[y] > dep[x]) std::swap(x,y); for(int i = 29;i >= 0;--i){ if(dep[fa[x][i]] >= dep[y]) x = fa[x][i]; } if(x == y) return x; for(int i = 29;i >= 0;--i){ if(fa[x][i] != fa[y][i]) x = fa[x][i],y = fa[y][i]; } return fa[x][0]; } ll T[N << 2]; #define ls(x) (x << 1) #define rs(x) (x << 1 | 1) #define mid ((l + r) >> 1) #define root 1,1,n inline void build(int u,int l,int r){ if(l == r){ T[u] = l;·1全額日圖與i哦怕【-】 return ; } build(ls(u),l,mid); build(rs(u),mid + 1,r); T[u] = lca(T[ls(u)],T[rs(u)]); return ; } inline ll qlca(int u,int l,int r,int tl,int tr){ if(tl <= l && r <= tr) return T[u]; ll li,ri;li = ri = 0; if(tl <= mid) li = qlca(ls(u),l,mid,tl,tr); if(tr > mid) ri = qlca(rs(u),mid + 1,r,tl,tr); return (li && ri) ? lca(li,ri) : li + ri; } //dfn_lca int H[N * 40]; int Hcnt; int Head[N],Ls[N * 40],Rs[N * 40]; inline void merge(int las,int &now,int p,int l,int r){ if(!now)now = ++Hcnt; Ls[now] = Ls[las]; Rs[now] = Rs[las]; H[now] = H[las] + 1; if(l == r) return ; if(p <= mid){ Ls[now] = 0; merge(Ls[las],Ls[now],p,l,mid); } if(p > mid){ Rs[now] = 0; merge(Rs[las],Rs[now],p,mid + 1,r); } } inline ll find(int las,int now,int l,int r,int tl,int tr){ if(tl <= l && r <= tr) return H[now] - H[las]; ll ans = 0; if(tl <= mid) ans += find(Ls[las],Ls[now],l,mid,tl,tr); if(tr > mid) ans += find(Rs[las],Rs[now],mid + 1,r,tl,tr); return ans; } //主席樹 ll las = 0; inline void solve(ll now,ll l,ll r){ ll num = find(Head[dfn[now] - 1],Head[end[now]],1,n,l,r); if(num == (r - l + 1)){ las = s[qlca(root,l,r)] - s[now]; std::cout<<las<<std::endl; }else{ if(num != 0){ std::cout<<(las = 0)<<std::endl; return ; } ll x = now; for(int i = 29;i >= 0;--i){ int p = fa[x][i]; if(p != 0){ if(!(find(Head[dfn[p] - 1],Head[end[p]],1,n,l,r))) x = fa[x][i]; } } if(fa[x][0] != 0 && !(find(Head[dfn[fa[x][0]] - 1],Head[end[fa[x][0]]],1,n,l,r))) x = fa[x][0]; x = fa[x][0]; ll L1 = qlca(root,l,r);ll L2 = lca(now,L1); if(L1 == L2) las = s[now] - s[x]; else las = s[now] - 2 * s[L2] + s[L1]; std::cout<<las<<std::endl; return ; } } int main(){ scanf("%d%d",&n,&q); for(int i = 1;i < n;++i){ ll x,y,w; scanf("%d%d%d",&x,&y,&w); add(x,y,w); add(y,x,w); } dfs(1,0); build(root); for(int i = 1;i <= n;++i) merge(Head[i - 1],Head[i],inv[i],1,n); while(q -- ){ ll p,l,r; scanf("%d%d%d",&p,&l,&r); p ^= las; l ^= las; r ^= las; solve(p,l,r); } }