bzoj 3626 : [LNOI2014]LCA (樹鏈剖分+線段樹)
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
思路: 表述不清。。。直接貼大佬的題解思路把
清華爺gconeice的題解:
顯然,暴力求解的複雜度是無法承受的。
考慮這樣的一種暴力,我們把 z 到根上的點全部打標記,對於 l 到 r 之間的點,向上搜尋到第一個有標記的點求出它的深度統計答案。觀察到,深度其實就是上面有幾個已標記了的點(包括自身)。所以,我們不妨把 z 到根的路徑上的點全部 +1,對於 l 到 r 之間的點詢問他們到根路徑上的點權和。仔細觀察上面的暴力不難發現,實際上這個操作具有疊加性,且可逆。也就是說我們可以對於 l 到 r 之間的點 i,將 i 到根的路徑上的點全部 +1, 轉而詢問 z 到根的路徑上的點(包括自身)的權值和就是這個詢問的答案。把詢問差分下,也就是用 [1, r] − [1, l − 1] 來計算答案,那麼現在我們就有一個明顯的解法。從 0 到 n − 1 依次插入點 i,即將 i 到根的路徑上的點全部+1。離線詢問答案即可。我們現在需要一個數據結構來維護路徑加和路徑求和,顯然樹鏈剖分或LCT 均可以完成這個任務。樹鏈剖分的複雜度為 O((n + q)· log n · log n),LCT的複雜度為 O((n + q)· log n),均可以完成任務。至此,題目已經被我們完美解決。
#include<bits/stdc++.h> using namespace std; #define ll long long #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define mid int m = (l + r) >> 1 const int mod = 201314; const int M = 1e5 + 10; struct node{ int to,next; }e[M]; int cnt,cnt1,n;int son[M],siz[M],head[M],fa[M],top[M],dep[M],tid[M]; int sum[M<<2],lazy[M<<2]; void add(int u,int v){ e[++cnt].to = v;e[cnt].next = head[u];head[u] = cnt; } void dfs1(int u,int faz,int deep){ dep[u] = deep; fa[u] = faz; siz[u] = 1; for(int i = head[u];i;i = e[i].next){ int v = e[i].to; if(v == faz) continue; dfs1(v,u,deep+1); siz[u] += siz[v]; if(siz[v] > siz[son[u]]||son[u] == -1) son[u] = v; } } void dfs2(int u,int t){ top[u] = t; tid[u] = ++cnt1; if(son[u] == -1) return ; dfs2(son[u],t); for(int i = head[u];i;i=e[i].next){ int v = e[i].to; if(v != fa[u]&&v != son[u]) dfs2(v,v); } } void pushup(int rt){ sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void pushdown(int l,int r,int rt){ if(lazy[rt]){ mid; sum[rt<<1] += (m-l+1)*lazy[rt]; sum[rt<<1|1] += (r-m)*lazy[rt]; lazy[rt<<1] += lazy[rt]; lazy[rt<<1|1] += lazy[rt]; lazy[rt] = 0; } } void build(int l,int r,int rt){ lazy[rt] = 0; if(l==r){ sum[rt] = lazy[rt] = 0; return ; } mid; build(lson); build(rson); pushup(rt); } void update(int L,int R,int l,int r,int rt){ if(L <= l&&R >= r){ sum[rt] += (r-l+1); lazy[rt] += 1; return ; } pushdown(l,r,rt); mid; if(L <= m) update(L,R,lson); if(R > m) update(L,R,rson); pushup(rt); } int query(int L,int R,int l,int r,int rt){ if(L <= l&&R >= r){ return sum[rt]; } pushdown(l,r,rt); mid; int ret = 0; if(L <= m) ret += query(L,R,lson); if(R > m) ret += query(L,R,rson); return ret; } void update(int x,int y){ int fx = top[x],fy = top[y]; while(fx != fy){ if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy); update(tid[fx],tid[x],1,n,1); x = fa[fx]; fx = top[x]; } if(dep[x] > dep[y]) swap(x,y); update(tid[x],tid[y],1,n,1); } int solve(int x,int y){ int fx = top[x],fy = top[y]; int ans = 0; while(fx != fy){ if(dep[fx] < dep[fy]) swap(x,y),swap(fx,fy); ans += query(tid[fx],tid[x],1,n,1); x = fa[fx]; fx = top[x]; } if(dep[x] > dep[y]) swap(x,y); ans += query(tid[x],tid[y],1,n,1); return ans; } struct node1{ int ans1,ans2,z; }q[M]; struct node2{ int pos,flag,id; }a[M]; bool cmp(node2 x,node2 y){ return x.pos < y.pos; } int main() { int m,l,r,x; cnt = 0;cnt1 = 0; scanf("%d%d",&n,&m); for(int i = 1;i < n;i ++){ scanf("%d",&x); add(x+1,i+1); } memset(son,-1,sizeof(son)); dfs1(1,0,1); dfs2(1,0); build(1,n,1); int tot = 0; for(int i = 1;i <= m;i ++){ scanf("%d%d%d",&l,&r,&q[i].z); l++;r++;q[i].z++; a[++tot].pos=l-1;a[tot].flag=0;a[tot].id=i; a[++tot].pos=r;a[tot].flag=1;a[tot].id=i; } int now = 1; sort(a+1,a+tot+1,cmp); for(int i = 1;i <= tot;i ++){ while(now <= a[i].pos){ update(1,now); now++; } int num = a[i].id; if(a[i].flag==0) q[num].ans1 = solve(1,q[num].z); else q[num].ans2 = solve(1,q[num].z); } for(int i = 1;i <= m;i ++){ int ans = (q[i].ans2-q[i].ans1)%mod; printf("%d\n",ans); } return 0; }