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

3626: [LNOI2014]LCA

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 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)]。
(即,求在[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。

  真是不知道那些人是如何想到这道题正解的,首先,区间查询都可以转化为前缀和的差值(就这个我都没想到),然后前缀和就想到了离线,按照点编号加点,查询[1,a]与z的答案,可以将z至根的路径赋值为1,询问[1,a]每个点到更路径点权和,而本题最神奇的地方是可以转化为将[1,a]所有点到根路径点权加1,询问z至根的点权和。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 51100
#define MAXE MAXN*2
#define MAXQ MAXN
#define MAXV MAXN
#define MOD 201314
#define lch (now<<1)
#define rch (now<<1^1)
int n,m;
struct sgt_node
{
        int l,r,sum,lzy;
}sgt[MAXN*4];
void up(int now)
{
        if (sgt[now].l==sgt[now].r)return;
        sgt[now].sum=(sgt[lch].sum+sgt[rch].sum)%MOD;
}
void down(int now)
{
        if (sgt[now].l==sgt[now].r)return;
        if (sgt[now].lzy)
        {
                sgt[lch].lzy+=sgt[now].lzy;
                sgt[rch].lzy+=sgt[now].lzy;
                sgt[lch].sum=(sgt[lch].sum+sgt[now].lzy*(sgt[lch].r-sgt[lch].l+1))%MOD;
                sgt[rch].sum=(sgt[rch].sum+sgt[now].lzy*(sgt[rch].r-sgt[rch].l+1))%MOD;
                sgt[now].lzy=0;
        }
}
void Build_sgt(int now,int l,int r)
{
        sgt[now].l=l;
        sgt[now].r=r;
        if (l==r)
        {
                return ;
        }
        Build_sgt(lch,l,(l+r)>>1);
        Build_sgt(rch,((l+r)>>1)+1,r);
}
void Add_sgt(int now,int l,int r)
{
        if (sgt[now].l==l && sgt[now].r==r)
        {
                sgt[now].sum+=sgt[now].r-sgt[now].l+1;
                sgt[now].lzy++;
                return ;
        }
        down(now);
        int mid=(sgt[now].l+sgt[now].r)>>1;
        if (r<=mid)
        {
                Add_sgt(lch,l,r);
        }else if (mid<l)
        {
                Add_sgt(rch,l,r);
        }else
        {
                Add_sgt(lch,l,mid);
                Add_sgt(rch,mid+1,r);
        }
        up(now);
}
int Qry_sgt(int now,int l,int r)
{
        if (sgt[now].l==l && sgt[now].r==r)
        {
                return sgt[now].sum%MOD;
        }
        int mid=(sgt[now].l+sgt[now].r)>>1;
        down(now);
        if (r<=mid)
                return Qry_sgt(lch,l,r);
        if (mid<l)
                return Qry_sgt(rch,l,r);
        return (Qry_sgt(lch,l,mid)+Qry_sgt(rch,mid+1,r))%MOD;
}
struct Edge
{
        int np;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope=-1;
void addedge(int x,int y)
{
        E[++tope].np=y;
        E[tope].next=V[x];
        V[x]=&E[tope];
}
int siz[MAXN];
int depth[MAXN];
int son[MAXN];
int fa[MAXN];
int top[MAXN];
int pos[MAXN],dfstime=0;
void dfs1(int now)
{
        Edge *ne;
        int mxsiz=0;
        siz[now]=1;
        for (ne=V[now];ne;ne=ne->next)
        {
                dfs1(ne->np);
                siz[now]+=siz[ne->np];
                if (siz[ne->np]>mxsiz)
                {
                        mxsiz=siz[ne->np];
                        son[now]=ne->np;
                }
        }
}
void dfs2(int now)
{
        Edge *ne;
        pos[now]=++dfstime;
        if (son[now])
        {
                top[son[now]]=top[now];
                dfs2(son[now]);
        }
        for (ne=V[now];ne;ne=ne->next)
        {
                if (ne->np==son[now])continue;
                top[ne->np]=ne->np;
                dfs2(ne->np);
        }
}
int q[MAXN];
struct qur_t
{
        int x,y,z;
        int ans;
}qur[MAXQ];
vector<pair<int,int> > vec[MAXN];
int main()
{
        freopen("input.txt","r",stdin);
        //freopen("output.txt","w",stdout);
        int x,y,z,i,j,k;
        scanf("%d%d",&n,&m);
        for (i=2;i<=n;i++)
        {
                scanf("%d",&x);x++;
                fa[i]=x;
                addedge(x,i);
        }
        dfs1(1);
        top[1]=1;
        dfs2(1);
        Build_sgt(1,1,n);
        for (i=0;i<m;i++)
        {
                scanf("%d%d%d",&x,&y,&z);
                x++;y++;z++;
                qur[i].x=x;
                qur[i].y=y;
                qur[i].z=z;
                vec[x-1].push_back(make_pair(z,0));
                vec[y].push_back(make_pair(z,0));
        }
        vector<pair<int,int> >::iterator it1;
        for (i=0;i<=n;i++)
        {
                sort(vec[i].begin(),vec[i].end());
                it1=unique(vec[i].begin(),vec[i].end());
                while (vec[i].end()!=it1)vec[i].pop_back();
        }
        for (i=0;i<vec[0].size();i++)
        {
                vec[0][i].second=0;
        }
        int ans;
        for (i=1;i<=n;i++)
        {
                x=i;
                while (x!=0)
                {
                        Add_sgt(1,pos[top[x]],pos[x]);
                        x=fa[top[x]];
                }
                for (j=0;j<vec[i].size();j++)
                {
                        x=vec[i][j].first;
                        ans=0;
                        while (x!=0)
                        {
                                ans=(ans+Qry_sgt(1,pos[top[x]],pos[x]))%MOD;
                                x=fa[top[x]];
                        }
                        vec[i][j].second=ans;
                }
        }
        for (i=0;i<m;i++)
        {
                ans=0;
                it1=lower_bound(vec[qur[i].x-1].begin(),vec[qur[i].x-1].end(),make_pair(qur[i].z,0));
                ans-=it1->second;
                it1=lower_bound(vec[qur[i].y].begin(),vec[qur[i].y].end(),make_pair(qur[i].z,0));
                ans+=it1->second;
                printf("%d\n",ans);
        }
}
时间: 2024-08-16 13:25:29

bzoj 3626: [LNOI2014]LCA 离线+树链剖分的相关文章

[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 和. 不仅满足可加性,还满足可减

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

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

理解了半天. 题解:http://hzwer.com/3891.html #include<bits/stdc++.h> #define N 50010 #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=1,int r=n,int k=1 using namespace

BZOJ 3626 LCA(离线+树链剖分)

首先注意到这样一个事实. 树上两个点(u,v)的LCA的深度,可以转化为先将u到根路径点权都加1,然后求v到根路径上的总点权值. 并且该题支持离线.那么我们可以把一个区间询问拆成两个前缀和形式的询问. 现在问题就变成了求[1,r]和x的LCA深度之和.实际上就是把[1,r]到根路径点权点1,然后求x到根路径上的总权值. 我们按编号从小往大依次加路径点权.然后就可以有序处理询问.用树链剖分维护的话,总复杂度为O((n+q)lognlogn).

bzoj3626: [LNOI2014]LCA (树链剖分)

很神奇的方法 感觉是有生之年都想不到正解的这种 考虑对i 到根的节点权值 + 1,则从根到z的路径和就是lca(i,z)的深度 所以依次把0 ~ n - 1的点权值 + 1 对于询问[l, r] 这个区间关于z 的深度和,就用(1, r) - (1, l - 1)的值表示 详见黄学长的博客啦 http://hzwer.com/3415.html 下面给出代码 #include <cstdio> #include <vector> #include <algorithm>

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 树链剖分 线段树 离线

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

LCA 倍增||树链剖分

方法1:倍增 1498ms #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> using namespace std; typedef long long ll; const int N=5e5+5; inline int read(){ char c=getchar();int x=0,f=1; while