BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

3626: [LNOI2014]LCA

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2050  Solved: 817
[Submit][Status][Discuss]

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 2
0
0
1
1
1 4 3
1 4 2

Sample Output

8
5

HINT

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

Source

数据已加强 by saffah


orz 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),均可以完成任务。至此,题目已经被我们完美解决。

z的LCA只能在z到根的路径上,统计到根的路径上每个点被作为LCA几次

很巧妙的转换,[l,r]所有点到根路径+1,然后询问z到根路径权值

显然树链剖分

多组询问,考虑将询问差分,[1, r] − [1, l − 1],然后离线询问的两个端点,每加到一个now将l-1或者r是now到询问都问一下

//
//  main.cpp
//  bzoj4196
//
//  Created by Candy on 2017/1/2.
//  Copyright © 2017年 Candy. All rights reserved.
//

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define lc o<<1
#define rc o<<1|1
#define m ((l+r)>>1)
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
const int N=1e5+5,MOD=201314;
typedef long long ll;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1; c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘; c=getchar();}
    return x*f;
}
int n,Q,u,l,r;
char s[20];
struct ques{
    int z,al,ar;
}q[N];
struct que{
    int x,id,isl;
    bool operator <(const que &r)const{return x<r.x;}
}a[N];
int p;
struct edge{
    int v,ne,c,f;
}e[N<<1];
int cnt,h[N];
inline void ins(int u,int v){
    cnt++;
    e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
}

int deep[N],fa[N],tid[N],tot,top[N],mx[N],size[N];
void dfs(int u){
    size[u]=1;
    for(int i=h[u];i;i=e[i].ne){
        int v=e[i].v;
        if(v==fa[u]) continue;
        fa[v]=u;deep[v]=deep[u]+1;
        dfs(v);
        size[u]+=size[v];
        if(size[v]>size[mx[u]]) mx[u]=v;
    }
}
void dfs(int u,int anc){
    if(!u) return;
    tid[u]=++tot;
    top[u]=anc;
    dfs(mx[u],anc);
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].v!=mx[u]&&e[i].v!=fa[u]) dfs(e[i].v,e[i].v);
}

struct node{
    int sum,add;
}t[N<<2];
inline void merge(int o){
    t[o].sum=t[lc].sum+t[rc].sum;
}
inline void paint(int o,int l,int r,int d){
    t[o].sum+=d*(r-l+1);
    t[o].add+=d;
}
inline void pushDown(int o,int l,int r){
    if(t[o].add){
        paint(lson,t[o].add);
        paint(rson,t[o].add);
        t[o].add=0;
    }
}
inline void segAdd(int o,int l,int r,int ql,int qr,int d){//printf("add %d %d %d\n",o,l,r);
    if(ql<=l&&r<=qr) paint(o,l,r,d);
    else{
        pushDown(o,l,r);
        if(ql<=m) segAdd(lson,ql,qr,d);
        if(m<qr) segAdd(rson,ql,qr,d);
        merge(o);
    }
}
inline int segQue(int o,int l,int r,int ql,int qr){//printf("que %d %d %d %d %d\n",o,l,r,ql,qr);
    if(ql<=l&&r<=qr) return t[o].sum;
    else{
        pushDown(o,l,r);
        int ans=0;
        if(ql<=m) ans+=segQue(lson,ql,qr);
        if(m<qr) ans+=segQue(rson,ql,qr);
        return ans;
    }
}
void build(int o,int l,int r){
    if(l==r) paint(o,l,r,0);
    else{
        build(lson);
        build(rson);
    }
}

void add(int x,int y,int d){
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        segAdd(1,1,n,tid[top[x]],tid[x],d);
        x=fa[top[x]];
    }
    if(tid[x]>tid[y]) swap(x,y);
    segAdd(1,1,n,tid[x],tid[y],d);
}
int query(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        ans+=segQue(1,1,n,tid[top[x]],tid[x]);ans%=MOD;
        x=fa[top[x]];
    }
    if(tid[x]>tid[y]) swap(x,y);
    ans+=segQue(1,1,n,tid[x],tid[y]);ans%=MOD;
    return ans;
}
int main(){
    n=read();Q=read();
    for(int i=2;i<=n;i++) u=read()+1,ins(u,i);
    dfs(1);dfs(1,1);
    build(1,1,n);
    for(int i=1;i<=Q;i++){
        l=read();r=read()+1;q[i].z=read()+1;
        a[++p].x=l;a[p].id=i;a[p].isl=1;
        a[++p].x=r;a[p].id=i;a[p].isl=0;
    }
    sort(a+1,a+1+p);
    int now=1;
    for(int i=1;i<=p;i++){//printf("a %d %d %d\n",a[i].x,a[i].id,a[i].isl);
        while(now<=a[i].x) add(1,now,1),now++;
        int _=a[i].id;
        if(a[i].isl) q[_].al=query(1,q[_].z);
        else q[_].ar=query(1,q[_].z);
    }
    for(int i=1;i<=Q;i++) printf("%d\n",(q[i].ar-q[i].al+MOD)%MOD);
}

在线的话可以用主席树,在dfs序上建主席树

这样每个线段树与历史版本的差距是一个节点,也就是一个点到根的路径,有logn段,每段有logn个新开节点,复杂度log^2n

好麻烦还要带标记啊

[2017-01-04 00:01:51]

我太弱了,不是M就是R,不玩了

时间: 2024-10-17 20:57:45

BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]的相关文章

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[

bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit][Status][Discuss] Description 一 棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

poj 3237 Tree(树链剖分,线段树)

Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7268   Accepted: 1969 Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edges are numbered 1 through N − 1. Each edge is associated with

HDU - 3966 Aragorn&#39;s Story(树链剖分入门+线段树)

HDU - 3966 Aragorn's Story Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of ene

BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线

http://www.lydsy.com/JudgeOnline/problem.php?id=3626 LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题,是时候复习一波离线算法泡脑子了.(没有暴力分的题,想不出来正解就爆零,太可怕了) 排序后离线操作通过前缀和计算答案,题解是hzwer的博客上复制的 http://hzwer.com/3891.html 直接引用清华爷gconeice的题解吧 显然,暴力求解的复杂度是无法承受的. 考虑这样的一种暴力

bzoj 3626: [LNOI2014]LCA 离线+树链剖分

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 426  Solved: 124[Submit][Status] 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)].(即

BZOJ 3626 [LNOI2014]LCA

一道比较神奇的题. 树链剖分+奇技淫巧: 神奇地发现,把z到跟的路径上的点值+1,查询一个点到跟的路径和就是它与z的lca的深度. 相对的,把l~r到跟的路径上的点值+1,查询z到跟的路径和就是要的答案. 考虑差分,把一个询问拆成两个,把所有询问排序然后从0~n-1到跟路径上的值+1: 一开始狂WA,发现把线段树区间加的(l-r)*v打成了(qr-ql)*v了... //Twenty #include<cstdio> #include<cstdlib> #include<io

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总

树链剖分处理+线段树解决问题 HDU 5029

http://acm.split.hdu.edu.cn/showproblem.php?pid=5029 题意:n个点的树,m次操作.每次操作输入L,R,V,表示在[L,R]这个区间加上V这个数字.比如[1,2]加上1,[1,3]加上1,那么1这个点就是{1,1},2也是{1,1},3是{1}.全部操作操作完之后,输出每个点中,最多的那个数字有几个.如果有相同的数字,就输出最小的那个数.比如{1,1,2,2},就输出1. 思路:树链剖分拍成链,然后通过找LCA,来离线预处理,然后最后通过离线暴力