【BZOJ 3626】【LNOI 2014】LCA【树链剖分】
2017-04-02 10:39
633 查看
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取模输出题解
此题实力树链剖分啊。首先,这道题在线没看到会做的人,但是离线也较难思考。
然后,我们先简化一下,对于一组询问怎么处理?
类似暴力的想法,我们把每要询问的点往上都打上标记,然后把z点往上找,第一次遇到的那个有标记的点就是它们的LCA。
然后就我们可以发现,每个标记的深度等同于它上面有多少个和它一样的标记。 那么我们可以把每个点到根的路径都+1,然后询问z到根的和,就是答案了。
很显然可以用树链剖分搞。
那么多组询问:
区间[l,r]的答案=区间[1,r]的答案-区间[1,l-1]的答案。
那么我们把每个询问拆成两个,一个+,一个-。 然后排序,按顺序从1~n开始更新即可。
代码
#include<cstdio> #include<cstring> #include<algorithm> #define N 50010 #define mod 201314 using namespace std; struct node{int to,next;}e[N*2]; int head ,m,tim,n; int dep ,size ,top ,fa ,son ,tid ; int ans ; struct data{int p,num,w,f;}a[N*2]; inline bool operator < (data a,data b) { return a.p < b.p; } void init() { tim = m = 0; memset(head,0,sizeof(head)); memset(son,-1,sizeof(son)); } void add_edge(int from,int to) { e[++m].next = head[from]; head[from] = m; e[m].to = to; } void dfs1(int v,int d) { dep[v] = d; size[v] = 1; for(int i = head[v];i;i=e[i].next) { dfs1(e[i].to,d+1); size[v] += size[e[i].to]; if(son[v] == -1 || size[e[i].to] > size[son[v]]) son[v] = e[i].to; } } void dfs2(int v,int tp) { tid[v] = ++tim; top[v] = tp; if(son[v] == -1) return; dfs2(son[v],tp); for(int i = head[v];i;i=e[i].next) if(e[i].to != son[v]) dfs2(e[i].to,e[i].to); } #define lson o << 1 #define rson o << 1 | 1 int sum[N << 2],add[N << 2]; void build(int o,int l,int r) { sum[o] = 0; if(l == r) return; int mid = (l+r)>>1; build(lson,l,mid); build(rson,mid+1,r); } void pushdown(int o,int l,int r) { if(add[o] != 0) { int mid = (l+r)>>1; add[lson] += add[o]; sum[lson] += (mid-l+1)*add[o]; add[rson] += add[o]; sum[rson] += (r-mid)*add[o]; add[o] = 0; } } void update(int o,int l,int r,int ll,int rr) { if(ll <= l && rr >= r) {add[o]++;sum[o]+=(r-l+1);return;} int mid = (l+r)>>1; pushdown(o,l,r); if(ll <= mid) update(lson,l,mid,ll,rr); if(rr > mid) update(rson,mid+1,r,ll,rr); sum[o] = sum[lson] + sum[rson]; } void change(int x,int y) { while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x,y); update(1,1,n,tid[top[x]],tid[x]); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x,y); update(1,1,n,tid[x],tid[y]); } int query(int o,int l,int r,int ll,int rr) { if(ll <= l && rr >= r)return sum[o]; int mid = (l+r)>>1,ans = 0; pushdown(o,l,r); if(ll <= mid) ans += query(lson,l,mid,ll,rr); if(rr > mid) ans += query(rson,mid+1,r,ll,rr); return ans; } int ask(int x,int y) { int ans = 0; while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x,y); ans += query(1,1,n,tid[top[x]],tid[x]); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x,y); ans += query(1,1,n,tid[x],tid[y]); return ans; } int main() { int q,u,v,z,tot = 0,now = 0; scanf("%d%d",&n,&q); init(); for(int i = 2;i <= n;i++) { scanf("%d",&fa[i]); fa[i]++; add_edge(fa[i],i); } dfs1(1,0); dfs2(1,1); build(1,1,n); for(int i = 1;i <= q;i++) { scanf("%d%d%d",&u,&v,&z); u++;v++;z++; a[++tot].p = u-1,a[tot].num = i,a[tot].w = z,a[tot].f = -1; a[++tot].p = v,a[tot].num = i,a[tot].w = z,a[tot].f = 1; } sort(a+1,a+tot+1); for(int i = 1;i <= tot;i++) { while(now < a[i].p) { now++; change(1,now); } ans[a[i].num] += a[i].f * ask(1,a[i].w); } for(int i = 1;i <= q;i++) printf("%d\n",(ans[i]+mod) % mod); return 0; }
相关文章推荐
- BZOJ 3626 LNOI 2014 LCA 树链剖分
- BZOJ 3626 LNOI 2014 LCA 树链剖分
- [bzoj 3626--LNOI2014]LCA
- 【BZOJ 3626】【LNOI 2014】LCA
- [BZOJ 3626] [LNOI2014] LCA 【树链剖分 + 离线 + 差分询问】
- bzoj3626: [LNOI2014]LCA 树链剖分
- BZOJ 3631 JLOI 2014 松鼠的新家 LCA 树链剖分
- 【BZOJ3626】LCA(树上差分,树链剖分)
- BZOJ 3612 HEOI2014 大工程 树链剖分求LCA的优越
- bzoj 3626: [LNOI2014]LCA 树链剖分
- BZOJ 3626 [LNOI2014]LCA - 树链剖分+线段树结构+玄学离散
- BZOJ 3626: [LNOI2014]LCA 树链剖分 询问差分
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
- bzoj 3626: [LNOI2014]LCA 离线+树链剖分
- BZOJ 3626 [LNOI2014]LCA ——树链剖分
- 【BZOJ3626】LCA(树链剖分,Link-Cut Tree)
- BZOJ 3626 LCA 树链剖分
- 【bzoj3626】[LNOI2014]LCA 树链剖分+线段树
- 【bzoj 3626】LCA(树链剖分)
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)