bzoj3626:[LNOI2014]LCA
題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=3626
Description
給出一個n個節點的有根樹(編號為0到n-1,根節點為0)。一個點的深度定義為這個節點到根的距離+1。
設dep[i]表示點i的深度,LCA(i,j)表示i與j的最近公共祖先。
有q次詢問,每次詢問給出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]區間內的每個節點i與z的最近公共祖先的深度之和)
Input
第一行2個整數n q。
接下來n-1行,分別表示點1到點n-1的父節點編號。
接下來q行,每行3個整數l r z。
Output
輸出q行,每行表示一個詢問的答案。每個答案對201314取模輸出
Sample Input
5 20
0
1
1
1 4 3
1 4 2
Sample Output
85
HINT
共5組數據,n與q的規模分別為10000,20000,30000,40000,50000。
題解:
樹鏈剖分,雖然題目是lca,然而好像并沒有什么關系。題目查詢的是在l到r區間中,與z的最近公共祖先的深度之和。然后很明顯可以知道兩點到根節點經過的共同節點個數為其公共祖先的深度,比如樣例中4和5共同經過1和2,而他們的最近公共祖先為2,其深度為2。然后求l到r之間的,可以轉換為[1,l]-[1,r-1]的差。那么就可以對于查詢離線,把詢問分成兩部分,按值來排序,比如[1,4,3],就分成[0,3]和[4,3]。查詢時把需要加的點一個個加進去,當到達一個點恰好為某個詢問的值,那么就可以把這個詢問的答案來更新一下了。值的定義為分成兩部分后區間的右端點,左端點恒為1。
代碼:
#include<iostream> #include<cmath> #include<cstring> #include<cstdlib> #include<cstdio> #include<ctime> #include<queue> #include<vector> #include<algorithm> #define mo 201314 #define N 300010 using namespace std; long long sum,ans[N]; int tot,p,q,n,u,v; long long lazy[N],b[N]; int siz[N],fa[N],son[N],dep[N],head[N],top[N],w[N]; struct h1{int next,to;}tu[N]; struct data{int l,f,w,wei;}a[2*N]; int getint() {int res=0,w=1;char ch=getchar();while ((ch>'9' || ch<'0')&&ch!='-') ch=getchar();if (ch=='-') w=-1,ch=getchar();while (ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();return res*w; } void link(int x,int y) {tu[++sum].next=head[x]; head[x]=sum; tu[sum].to=y; } void dfs1(int p) {siz[p]=1;for (int i=head[p];i;i=tu[i].next){int v=tu[i].to;if (v==fa[p]) continue;dep[v]=dep[p]+1;dfs1(v); siz[p]+=siz[v]; if (siz[v]>siz[son[p]]) son[p]=v;} } void dfs2(int p,int t) {top[p]=t; w[p]=++tot;if (son[p]) dfs2(son[p],t);for (int i=head[p];i;i=tu[i].next)if (tu[i].to!=son[p]&&tu[i].to!=fa[p]) dfs2(tu[i].to,tu[i].to); } bool cmp(data a,data b) {return a.l<b.l;} void tree_add(int x,int l,int r,int L,int R) {if (l==L&&r==R) lazy[x]=(lazy[x]+1)%mo;else{b[x]=(b[x]+R-L+1)%mo;int mid=(l+r)>>1;if (L>mid) tree_add((x<<1)+1,mid+1,r,L,R);else if (R<=mid) tree_add(x<<1,l,mid,L,R);else tree_add(x<<1,l,mid,L,mid),tree_add((x<<1)+1,mid+1,r,mid+1,R);} } void tree_find(int x,int l,int r,int L,int R) {if (l==L&&r==R) sum=(sum+b[x]+1LL*lazy[x]*(r-l+1))%mo;else{int mid=(l+r)>>1;lazy[x<<1]=(lazy[x<<1]+lazy[x])%mo;lazy[(x<<1)+1]=(lazy[(x<<1)+1]+lazy[x])%mo;b[x]=(b[x]+1LL*lazy[x]*(r-l+1))%mo;lazy[x]=0;if (R<=mid) tree_find(x<<1,l,mid,L,R);else if (L>mid) tree_find((x<<1)+1,mid+1,r,L,R);else tree_find(x<<1,l,mid,L,mid),tree_find((x<<1)+1,mid+1,r,mid+1,R);} } void add(int p) {while (p!=0){tree_add(1,1,n,w[top[p]],w[p]);p=fa[top[p]];} } void find(int p) {while (p!=0){tree_find(1,1,n,w[top[p]],w[p]);p=fa[top[p]];} } int main() {n=getint(); q=getint();for (int i=2;i<=n;i++){p=getint()+1;fa[i]=p; link(p,i);}dep[1]=1; dfs1(1);dfs2(1,1); tot=0;for (int i=1;i<=q;i++){u=getint()+1; v=getint()+1; p=getint()+1;a[++tot].l=u-1; a[tot].f=-1; a[tot].wei=i; a[tot].w=p;a[++tot].l=v; a[tot].f=1; a[tot].wei=i; a[tot].w=p;}sort(a+1,a+1+tot,cmp);u=0; for (int i=1;i<=tot;i++){while (u<a[i].l) u++,add(u);sum=0; find(a[i].w);ans[a[i].wei]=(ans[a[i].wei]+1LL*sum*a[i].f+mo)%mo;}for (int i=1;i<=q;i++)printf("%lld\n",ans[i]);return 0; }?
轉載于:https://www.cnblogs.com/xiaoqiang200015/p/6016006.html
總結
以上是生活随笔為你收集整理的bzoj3626:[LNOI2014]LCA的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sublime 自定义快捷键
- 下一篇: BroadcastReceive之ip拨