BZOJ3626 LCA

2 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
有q次询问,每次询问给出l r z,求 ri=ldeep[LCA(i,z)]

Solution

我们可以发现,这个答案是可减得。所以对每次询问,只要求出 (ri=1) - (l1i=1) 即可。
对于x,z的LCA的深度。就是x,z到根路径中点的交集的点数。所以把x到根的路径上每个点+1,再询问z到根的路径和就可以。对每个询问的l,r,我们按照大小依次加点并查询就可以。
所以问题就变成了维护两种操作:x到根的路径都+1,询问x到根的路径的和。用树链剖分维护。

代码
#include<cstdio>
#include<algorithm>
#define mo 201314
using namespace std;
struct node{
    int x,z,id,c;
}q[100010];
typedef long long ll;
int next[300010],num,head[300010],vet[300010],tag[300010],top[300010],size[300010],H_son[300010],fa[300010];
ll tree[300010],Ans[300010];
int ti,x[100010],dfn[100010],n,deep[100010];
bool cmp(node x,node y)
{
    return x.x<y.x;
}
void add(int u,int v)
{
    next[++num]=head[u];
    head[u]=num;
    vet[num]=v;
}
void dfs(int u)
{
    size[u]=1;
    int Max=0,Maxs=0;
    for (int i=head[u];i;i=next[i])
    {
        int v=vet[i];
        if (v==fa[u]) continue;
        dfs(v);
        if (size[v]>Max) Max=size[v],Maxs=v;
        size[u]+=size[v];
    }
    H_son[u]=Maxs;
}
void dfs(int u,int Top)
{
    dfn[u]=++ti;
    x[ti]=u;
    top[u]=Top;
    if (H_son[u])
        dfs(H_son[u],Top);
    for (int i=head[u];i;i=next[i])
    {
        int v=vet[i];
        if (v!=H_son[u]&&v!=fa[u])
            dfs(v,v);
    }
}
void pushdown(int t,int l,int r)
{
    if (!tag[t]) return;
    int mid=(l+r)>>1;
    tree[t<<1]+=(ll)tag[t]*(mid-l+1);
    tree[t<<1|1]+=(ll)tag[t]*(r-mid);
    tag[t<<1]+=tag[t];
    tag[t<<1|1]+=tag[t];
    tag[t]=0;
}
ll query(int l,int r,int x,int y,int t)
{
    if (l==x&&y==r)
        return tree[t];
    pushdown(t,l,r);
    int mid=(l+r)>>1;
    if (y<=mid) return query(l,mid,x,y,t<<1);
    else if (x>mid) return query(mid+1,r,x,y,t<<1|1);
    else return query(l,mid,x,mid,t<<1)+query(mid+1,r,mid+1,y,t<<1|1);
}
void modify(int l,int r,int x,int y,int t,int key)
{
    if (l==x&&y==r)
    {
        tag[t]+=key;
        tree[t]+=(ll)key*(r-l+1);
        return;
    }
    pushdown(t,l,r);
    int mid=(l+r)>>1;
    if (y<=mid) modify(l,mid,x,y,t<<1,key);
    else if (x>mid) modify(mid+1,r,x,y,t<<1|1,key);
    else modify(l,mid,x,mid,t<<1,key),modify(mid+1,r,mid+1,y,t<<1|1,key);
    tree[t]=tree[t<<1]+tree[t<<1|1];
}
ll solve(int x)
{
    ll ans=0;
    while (x)
    {
        ans+=query(1,n,dfn[top[x]],dfn[x],1);
        x=fa[top[x]];
    }
    return ans;
}
void Change(int x)
{
    while (x)
    {
        modify(1,n,dfn[top[x]],dfn[x],1,1);
        x=fa[top[x]];
    }
}
int main()
{
    int m;
    scanf("%d%d",&n,&m);
    for (int i=2;i<=n;i++)
    {
        int u;
        scanf("%d",&u);
        u++;
        fa[i]=u;
        add(u,i);
        add(i,u);
    }
    dfs(1);
    deep[1]=1;
    dfs(1,1);
    int cnt=0;
    for (int i=1;i<=m;i++)
    {
        int L,R,Z;
        scanf("%d%d%d",&L,&R,&Z);
        L++,R++,Z++;
        q[++cnt].x=L-1,q[cnt].z=Z,q[cnt].c=-1,q[cnt].id=i;
        q[++cnt].x=R,q[cnt].z=Z,q[cnt].c=1,q[cnt].id=i;
    }
    sort(q+1,q+1+cnt,cmp);
    for (int i=1;i<=q[1].x;i++)
        Change(i);
    Ans[q[1].id]+=solve(q[1].z)*(ll)q[1].c;
    q[1].x=max(q[1].x,1);
    for (int i=2;i<=cnt;i++)
    {
        for (int j=q[i-1].x+1;j<=q[i].x;j++)
            Change(j);
        Ans[q[i].id]+=solve(q[i].z)*(ll)q[i].c; 
    }
    for (int i=1;i<=m;i++)
        printf("%lld\n",Ans[i]%mo);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值