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<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
typedef long long LL;
using namespace std;
const int mod=201314;
const int N=200000+299;
int n,m,cnt,l,r,z;
LL ans[N];
struct qs{
    int id,x,f,ts,z;
    qs(){}
    qs(int id,int x,int f,int ts,int z):id(id),x(x),f(f),ts(ts),z(z){}
    friend bool operator <(const qs&a,const qs&b) {
        return a.x<b.x;
    }
}q[N]; 

int ecnt,fir[N],nxt[N],to[N];

void add(int u,int v) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}

#define lc x<<1
#define rc x<<1|1
#define mid ((l+r)>>1)
LL sg[N<<2],lz[N<<2];

void down(int x,int l_len,int r_len) {
     if(!lz[x]) return;
     if(lc) (sg[lc]+=l_len*lz[x])%=mod,(lz[lc]+=lz[x])%=mod;
     if(rc) (sg[rc]+=r_len*lz[x])%=mod,(lz[rc]+=lz[x])%=mod;
     lz[x]=0;
}

void add(int x,int l,int r,int ql,int qr,LL v) {
    if(l>=ql&&r<=qr) { (sg[x]+=v*(r-l+1))%=mod; (lz[x]+=v)%=mod; return;}
    down(x,mid-l+1,r-mid);
    if(ql<=mid) add(lc,l,mid,ql,qr,v);
    if(qr>mid) add(rc,mid+1,r,ql,qr,v);
    sg[x]=sg[lc]+sg[rc];
    if(sg[x]>=mod) sg[x]-=mod;
}

LL query(int x,int l,int r,int ql,int qr) {
    if(l>=ql&&r<=qr) return sg[x];
    down(x,mid-l+1,r-mid);
    if(qr<=mid) return query(lc,l,mid,ql,qr);
    if(ql>mid) return query(rc,mid+1,r,ql,qr);
    LL res=query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
    return res>=mod?res-mod:res;
}

int R[N],sz[N];

void DFS(int x) {
    sz[x]=1;
    for(int i=fir[x];i;i=nxt[i]) {
        R[to[i]]=R[x]+1;
        DFS(to[i]);
        sz[x]+=sz[to[i]];
    }
}

int tot,top[N],fa[N],tid[N];

void dfs(int x,int t) {
    top[x]=t;
    tid[x]=++tot;
    int mson=0;
    for(int i=fir[x];i;i=nxt[i])
        if(!mson||sz[to[i]]>=sz[mson]) mson=to[i];
    if(!mson) return;
    dfs(mson,t);
    for(int i=fir[x];i;i=nxt[i])
    if(to[i]!=mson) dfs(to[i],to[i]);
}

void schange(int l,int r,int v) {
    while(top[l]!=top[r]) {
        if(R[top[l]]<R[top[r]]) swap(l,r);
        add(1,1,n,tid[top[l]],tid[l],v);
        l=fa[top[l]];
    }
    if(tid[l]>tid[r]) swap(l,r);
    add(1,1,n,tid[l],tid[r],v);
}

LL squery(int l,int r) {
    LL res=0;
    while(top[l]!=top[r]) {
        if(R[top[l]]<R[top[r]]) swap(l,r);
        res+=query(1,1,n,tid[top[l]],tid[l]);
        if(res>=mod) res-=mod;
        l=fa[top[l]];
    }
    if(tid[l]>tid[r]) swap(l,r);
    res+=query(1,1,n,tid[l],tid[r]);
    if(res>=mod) res-=mod;
    return res;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++) {
        scanf("%d",&fa[i]);
        add(fa[i],i);
    }
    DFS(0);
    dfs(0,0);
    for(int i=1;i<=m;i++) {
        scanf("%d%d%d",&l,&r,&z);
        if(l) q[++cnt]=qs(i,l-1,-1,0,z);
        q[++cnt]=qs(i,r,1,0,z);
    }
    sort(q+1,q+cnt+1);
    int now=1;
    for(int i=0;i<n;i++) {
        schange(0,i,1);
        while(now<=m*2&&q[now].x<=i) {
            LL res=squery(0,q[now].z);
            ans[q[now].id]+=q[now].f*res;
            now++;
            if(now>2*m) break;
        }
    }
    for(int i=1;i<=m;i++)
        printf("%lld\n",(ans[i]+mod)%mod);
    return 0;
}

时间: 2024-08-02 09:50:45

BZOJ 3626 [LNOI2014]LCA的相关文章

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

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 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[LC

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 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 【树链剖分 + 离线 + 差分询问】

题目链接: BZOJ - 3626 题目分析 考虑这样的等价问题,如果我们把一个点 x 到 Root 的路径上每个点的权值赋为 1 ,其余点的权值为 0,那么从 LCA(x, y) 的 Depth 就是从 y 到 Root 的路径上的点权和. 这个方法是可以叠加的,这是非常有用的一点.如果我们把 [l, r] 的每个点到 Root 的路径上所有点的权值 +1,再求出从 c 到 Root 的路径点权和,即为 [l, r] 中所有点与 c 的 LCA 的 Depth 和. 不仅满足可加性,还满足可减

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

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. O

【BZOJ 3626】 [LNOI2014]LCA

3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MB Submit: 735 Solved: 250 [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[L

[LNOI2014]LCA

[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的最近公共祖先的深度之和) INPUT 第一行2个整数n q.接下来n-1行,分别表示点1到点n-1的父节点编号.接下来q行,每行3个整数l r z. O

【BZOJ3626】[LNOI2014]LCA 离线+树链剖分+线段树

[BZOJ3626][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的父节点编号.接