【做题记录】P4211 [LNOI2014]LCA
P4211 [LNOI2014]LCA
題意
給出一個 \(n\) 個節點的有根樹(編號為 \(0\) 到 \(n-1\),根節點為 \(0\))。
一個點的深度定義為這個節點到根的距離 \(+1\)。
設 \(dep[i]\) 表示點 \(i\) 的深度,\(LCA(i,j)\) 表示 \(i\) 與 \(j\) 的最近公共祖先。
有 \(m\) 次詢問,每次詢問給出 l r z,求 \(\sum_{i=l}^r dep[LCA(i,z)]\)。
題解
我們考慮這樣一件事情,就是說,兩個點的 \(Lca\) 的深度的意義到底是什么,如何才能夠快速求的?
在我們不會求 \(Lca\) 的時候,我們會將一個點一個一個往上爬,并將路徑上的點染色。之后我們將另一個點暴力往上跳,第一個染色過的點就是 \(Lca\)。
我們考慮到這樣一件事情,如果我們將一個點到根的路徑上每個點數值加 \(1\),再求得另一個點到根的路徑上的權值和,這不就是 \(Lca\) 的深度了嗎?
于是求解一次詢問就相當于對于 \(l\le x\le r\),將 \(x\) 到根的路徑上每個點加一,最后答案就是 \(z\) 到根的權值和。
由于詢問次數很多,不可能每次都這樣處理一遍,考慮將詢問拆開。
\[\sum_{i=l}^r dep[LCA(i,z)]=\sum_{i=1}^r dep[LCA(i,z)]-\sum_{i=1}^{l-1} dep[LCA(i,z)] \]這樣我們就可以在 \(O(n\log n)\) 復雜度內解決問題啦!
這就叫數據結構二步曲,學習一下,代碼我現在放下。
#define Maxn 50005 #define pb push_back #define mod 201314 typedef long long ll; int n,m,tot,Time,cnt; int ans[Maxn]; int dfn[Maxn],tp[Maxn],fa[Maxn],dep[Maxn],siz[Maxn],bigson[Maxn]; int hea[Maxn],nex[Maxn],ver[Maxn]; struct TREE { int laz,sum; }tree[Maxn<<2]; struct Query { int num,z,opt; }; vector<Query> q[Maxn]; void dfs1(int x) {siz[x]=1;for(int i=hea[x];i;i=nex[i]){fa[ver[i]]=x,dep[ver[i]]=dep[x]+1;dfs1(ver[i]),siz[x]+=siz[ver[i]];if(siz[ver[i]]>siz[bigson[x]]) bigson[x]=ver[i];} } void dfs2(int x,int T) {tp[x]=T,dfn[x]=++Time;if(bigson[x]) dfs2(bigson[x],T);for(int i=hea[x];i;i=nex[i]){if(ver[i]==bigson[x]) continue;dfs2(ver[i],ver[i]);} } void pushdown(int p,int nl,int nr) {int mid=(nl+nr)>>1;tree[p<<1].laz=(tree[p<<1].laz+tree[p].laz)%mod;tree[p<<1|1].laz=(tree[p<<1|1].laz+tree[p].laz)%mod;tree[p<<1].sum=(tree[p<<1].sum+1ll*tree[p].laz*(mid-nl+1)%mod)%mod;tree[p<<1|1].sum=(tree[p<<1|1].sum+1ll*tree[p].laz*(nr-mid)%mod)%mod;tree[p].laz=0; } void add(int p,int nl,int nr,int l,int r) {if(nl>=l && nr<=r) { tree[p].laz++,tree[p].sum+=(nr-nl+1); return; }pushdown(p,nl,nr);int mid=(nl+nr)>>1;if(mid>=l) add(p<<1,nl,mid,l,r);if(mid<r) add(p<<1|1,mid+1,nr,l,r);tree[p].sum=(tree[p<<1].sum+tree[p<<1|1].sum)%mod; } int query(int p,int nl,int nr,int l,int r) {if(nl>=l && nr<=r) return tree[p].sum;pushdown(p,nl,nr);int mid=(nl+nr)>>1,ret=0;if(mid>=l) ret=(ret+query(p<<1,nl,mid,l,r))%mod;if(mid<r) ret=(ret+query(p<<1|1,mid+1,nr,l,r))%mod;tree[p].sum=(tree[p<<1].sum+tree[p<<1|1].sum)%mod;return ret; } inline void add_path(int x) {while(tp[x]!=tp[1]) add(1,1,n,dfn[tp[x]],dfn[x]),x=fa[tp[x]];add(1,1,n,dfn[1],dfn[x]); } inline int query_path(int x) {int ret=0;while(tp[x]!=tp[1]) ret=(ret+query(1,1,n,dfn[tp[x]],dfn[x]))%mod,x=fa[tp[x]];ret=(ret+query(1,1,n,dfn[1],dfn[x]))%mod;return ret; } inline void add_edge(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; } int main() {n=rd(),m=rd();for(int i=2,f;i<=n;i++) f=rd()+1,add_edge(f,i);dfs1(1),dfs2(1,0); // 記得輸入的下標從 0 開始 for(int i=1,l,r,z;i<=m;i++){l=rd()+1,r=rd()+1,z=rd()+1;q[l-1].pb((Query){i,z,-1});q[r].pb((Query){i,z,1});}for(int i=1;i<=n;i++){add_path(i);for(Query j:q[i])ans[j.num]=(ans[j.num]+j.opt*query_path(j.z)%mod+mod)%mod;}for(int i=1;i<=m;i++) printf("%d\n",ans[i]);return 0; }總結
以上是生活随笔為你收集整理的【做题记录】P4211 [LNOI2014]LCA的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 七夕祝福语简短一句话
- 下一篇: 为生活努力打拼的句子短句