BZOJ3626[LNOI2014]LCA——樹鏈剖分+線段樹
題目描述
給出一個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的最近公共祖先的深度之和)
輸入
第一行2個整數n q。
接下來n-1行,分別表示點1到點n-1的父節點編號。
接下來q行,每行3個整數l r z。
輸出
輸出q行,每行表示一個詢問的答案。每個答案對201314取模輸出
樣例輸入
0
0
1
1
1 4 3
1 4 2
樣例輸出
85
提示
共5組數據,n與q的規模分別為10000,20000,30000,40000,50000。
兩個點a,b的lca的深度就是dep[a]+dep[b]-2*dep[lca],如果暴力地寫這道題就是對於每個x與[l,r]內所有數的lca都求一遍,但可以發現lca還有一種求法:對於i,x兩點的lca,可以把i到根節點路徑上所有的邊權+1(剛開始都是零),只要再求x到根節點上的路徑和就是lca的深度。那麽對於[l,r]內所有的點和x的lca,只要把每個點到根的路徑上邊權都+1,然後再求x到根的路徑和就好了。這個只要樹鏈剖分加線段樹就能維護,每次修改和查詢在樹上邊跳邊在線段樹中操作就行了。但對於每次詢問都要把線段樹清空再重新標記,顯然還是不行的,因此可以離線來做。我們發現求的東西具有可減性,即求[l,r]與x的lca深度和等於求[1,r]與x的lca深度和-[1,l-1]與x的lca深度和。因此每個詢問可以拆成兩部分,然後把所有查詢排序,按節點標號順序對到根路徑上的邊+1,每到一個點處理這個點處對應的查詢。註意點的編號從零開始。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; int x; int l,r; int n,m; int tot; int num; int cnt; int f[100010]; int d[100010]; int s[100010]; bool g[100010]; int a[1000010]; int to[100010]; ll sum[800010]; ll ans[100010]; int top[100010]; int son[100010]; int size[100010]; int head[100010]; int next[100010]; struct node { int x; int l; int id; }q[200010]; bool cmp(node a,node b) { return a.l<b.l; } void add(int x,int y) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; } void dfs(int x) { size[x]=1; for(int i=head[x];i;i=next[i]) { d[to[i]]=d[x]+1; f[to[i]]=x; dfs(to[i]); size[x]+=size[to[i]]; if(size[to[i]]>size[son[x]]) { son[x]=to[i]; } } } void dfs2(int x,int tp) { s[x]=++num; top[x]=tp; if(son[x]) { dfs2(son[x],tp); } for(int i=head[x];i;i=next[i]) { if(to[i]!=son[x]) { dfs2(to[i],to[i]); } } } void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void pushdown(int rt,int l,int r) { if(a[rt]) { int mid=(l+r)>>1; a[rt<<1]+=a[rt]; a[rt<<1|1]+=a[rt]; sum[rt<<1]+=1ll*a[rt]*(mid-l+1); sum[rt<<1|1]+=1ll*a[rt]*(r-mid); a[rt]=0; } } void change(int rt,int l,int r,int L,int R) { if(L<=l&&r<=R) { a[rt]++; sum[rt]+=1ll*(r-l+1); return ; } pushdown(rt,l,r); int mid=(l+r)>>1; if(L<=mid) { change(rt<<1,l,mid,L,R); } if(R>mid) { change(rt<<1|1,mid+1,r,L,R); } pushup(rt); } ll query(int rt,int l,int r,int L,int R) { if(L<=l&&r<=R) { return sum[rt]; } pushdown(rt,l,r); int mid=(l+r)>>1; ll res=0; if(L<=mid) { res+=query(rt<<1,l,mid,L,R); } if(R>mid) { res+=query(rt<<1|1,mid+1,r,L,R); } return res; } void updata(int x) { while(top[x]!=1) { change(1,1,n,s[top[x]],s[x]); x=f[top[x]]; } change(1,1,n,1,s[x]); } ll downdata(int x) { ll res=0; while(top[x]!=1) { res+=query(1,1,n,s[top[x]],s[x]); x=f[top[x]]; } res+=query(1,1,n,1,s[x]); return res; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { scanf("%d",&x); add(x+1,i+1); } dfs(1); dfs2(1,1); for(int i=1;i<=m;i++) { scanf("%d%d%d",&l,&r,&x); x++; l++; r++; q[++cnt].l=l-1; q[cnt].x=x; q[cnt].id=i; q[++cnt].l=r; q[cnt].x=x; q[cnt].id=i; } sort(q+1,q+1+cnt,cmp); int now=1; for(int i=0;i<=n;i++) { if(i!=0) { updata(i); } while(q[now].l==i&&now<=cnt) { if(g[q[now].id]==0) { ans[q[now].id]-=downdata(q[now].x); g[q[now].id]=1; } else { ans[q[now].id]+=downdata(q[now].x); } now++; } } for(int i=1;i<=m;i++) { printf("%lld\n",ans[i]%201314); } }
BZOJ3626[LNOI2014]LCA——樹鏈剖分+線段樹