【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[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

思路题+树链剖分。

对于一个询问 r ,我们可以从 r 开始一直向 r 的祖先走,直到走到根,因为 r 与其他节点的LCA必然是这些节点中的一个。

当走到某个结点时,如果他的子孙中包含了 i (第一次),那么这个节点就是 i r 的LCA。

基于这个思路,我们来考虑这道题。

将询问离线,从0~n-1依次插入每一个点,插入时把 i 点到根的路径上的点权值全部 +1 ,可以发现询问 i 的点与 r 的LCA深度之和就是r到根路径上的权值之和,用树链剖分即可实现。

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define LL long long
#define M 100005
using namespace std;
int T,id[M],tot=0,now=0,h[M],dep[M],f[M],son[M],size[M],top[M],n;
LL ans[M];
struct Query
{
    int v,z,id;
    LL ans;
}q1[M],q2[M];
struct edge
{
    int y,ne;
}e[M*2];
struct Segtree
{
    int l,r;
    LL v,s;
}t[M*4];
bool cmp(Query a,Query b)
{
    return a.v<b.v;
}
void read(int &tmp)
{
    tmp=0;
    char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar());
    for (;ch>='0'&&ch<='9';ch=getchar())
        tmp=tmp*10+ch-'0';
}
void Addedge(int x,int y)
{
    e[++tot].y=y;
    e[tot].ne=h[x];
    h[x]=tot;
}
void dfs1(int x,int fa,int d)
{
    dep[x]=d;
    f[x]=fa;
    son[x]=0;
    size[x]=1;
    for (int i=h[x];i;i=e[i].ne)
    {
        int y=e[i].y;
        dfs1(y,x,d+1);
        size[x]+=size[y];
        if (size[son[x]]<size[y])
            son[x]=y;
    }
}
void dfs2(int x,int tp)
{
    top[x]=tp;
    id[x]=++now;
    if (son[x])
        dfs2(son[x],tp);
    for (int i=h[x];i;i=e[i].ne)
    {
        int y=e[i].y;
        if (y==son[x]) continue;
        dfs2(y,y);
    }
}
void Build(int x,int l,int r)
{
    t[x].l=l,t[x].r=r;
    t[x].s=t[x].v=0;
    if (l==r) return;
    int m=(l+r)>>1;
    Build(x<<1,l,m);
    Build((x<<1)+1,m+1,r);
}
void Push_down(int x)
{
    t[x<<1].v+=t[x].v,t[(x<<1)+1].v+=t[x].v;
    LL k1=t[x<<1].r-t[x<<1].l+1,k2=t[(x<<1)+1].r-t[(x<<1)+1].l+1;
    t[x<<1].s+=(k1*t[x].v),t[(x<<1)+1].s+=(k2*t[x].v);
    t[x].v=0;
}
void Push_up(int x)
{
    t[x].s=t[x<<1].s+t[(x<<1)+1].s;
}
void add(int x,int l,int r)
{
    if (t[x].l>=l&&t[x].r<=r)
    {
        t[x].v++;
        t[x].s+=(t[x].r-t[x].l+1);
        return;
    }
    if (t[x].v) Push_down(x);
    int m=(t[x].l+t[x].r)>>1;
    if (l<=m) add(x<<1,l,r);
    if (r>m) add((x<<1)+1,l,r);
    Push_up(x);
}
void Add(int x)
{
    int tp1=top[x];
    while (tp1)
    {
        add(1,id[tp1],id[x]);
        x=f[tp1];
        tp1=top[x];
    }
    add(1,1,id[x]);
}
LL getsum(int x,int l,int r)
{
    if (t[x].l>=l&&t[x].r<=r)
        return t[x].s;
    if (t[x].v) Push_down(x);
    int m=(t[x].l+t[x].r)>>1;
    LL ans=0;
    if (l<=m) ans+=getsum(x<<1,l,r);
    if (r>m) ans+=getsum((x<<1)+1,l,r);
    return ans;
}
LL Getsum(int x)
{
    int tp1=top[x];
    LL ans=0;
    while (tp1)
    {
        ans+=getsum(1,id[tp1],id[x]);
        x=f[tp1];
        tp1=top[x];
    }
    return ans+getsum(1,1,id[x]);
}
int main()
{
    read(n),read(T);
    for (int i=1;i<n;i++)
    {
        int x;
        read(x);
        Addedge(x+1,i+1);
    }
    for (int i=1;i<=T;i++)
    {
        read(q1[i].v),read(q2[i].v),read(q1[i].z);
        q1[i].z++;
        q2[i].z=q1[i].z;
        q2[i].v++;
        q1[i].id=q2[i].id=i;
    }
    sort(q1+1,q1+1+T,cmp);
    sort(q2+1,q2+1+T,cmp);
    dfs1(1,0,1),dfs2(1,0);
    Build(1,1,n);
    int l1=0,l2=0;
    q1[T+1].v=q2[T+1].v=n+1;
    for (int i=1;i<=n;i++)
    {
        Add(i);
        for (;q1[l1].v<=i;l1++)
        {
            if (!q1[l1].v) q1[l1].ans=0;
            else q1[l1].ans=Getsum(q1[l1].z);
        }
        for (;q2[l2].v<=i;l2++)
            q2[l2].ans=Getsum(q2[l2].z);
    }
    for (int i=1;i<=T;i++)
        ans[q1[i].id]-=q1[i].ans,ans[q2[i].id]+=q2[i].ans;
    for (int i=1;i<=T;i++)
        printf("%d\n",(int)(ans[i]%201314LL));
    return 0;
}

一直WA是因为树链剖分中x=f[tp1],tp1=top[x]写错了

你可能感兴趣的:(【BZOJ 3626】 [LNOI2014]LCA)