日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【做题记录】P4211 [LNOI2014]LCA

發布時間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【做题记录】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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。