【BZOJ3626】LCA(LNOI2014)-树链剖分+离线处理

测试地址:LCA
做法:本题需要用到树链剖分+离线处理。
题目中要求的式子看上去非常难求,怎么办呢?
我们考虑求 z z 的每个祖先对答案的贡献。令z i i 级祖先为fa(z,i) siz(v,l,r) s i z ( v , l , r ) 为以 v v 点为根的子树中有多少个编号在[l,r]中的点,那么式子可以转化成:
dep(z)1i=0dep(fa(z,i))(siz(fa(z,i),l,r)siz(fa(z,i1),l,r)) ∑ i = 0 d e p ( z ) − 1 d e p ( f a ( z , i ) ) ( s i z ( f a ( z , i ) , l , r ) − s i z ( f a ( z , i − 1 ) , l , r ) )
因为 dep(fa(z,i))=dep(fa(z,i1))1 d e p ( f a ( z , i ) ) = d e p ( f a ( z , i − 1 ) ) − 1 ,所以上式可以转化为:
dep(z)1i=0siz(fa(z,i),l,r) ∑ i = 0 d e p ( z ) − 1 s i z ( f a ( z , i ) , l , r )
我们发现,如果我们对每个点 v v 维护siz(v,l,r),我们就可以通过树链剖分来统计答案。然而我们发现这个不太好维护,但把它改成维护 siz(v,0,r)siz(v,0,l1) s i z ( v , 0 , r ) − s i z ( v , 0 , l − 1 ) 的前缀和形式就明朗多了,对于一个点 v v ,插入后会对它的所有祖先的siz做出 1 1 的贡献,这个显然能用树链剖分维护,因此我们离线处理所有询问,按编号从小到大插入点,然后对影响到的询问做出修改即可,时间复杂度为O(nlog2n)
我傻逼的地方:一开始居然没有看到对 201314 201314 取模……太傻逼了……
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,first[50010]={0},tot=0,tim=0;
ll ans[50010]={0},seg[200010]={0},p[200010]={0};
int fa[50010],siz[50010],son[50010],top[50010],pos[50010];
struct edge
{
    int v,next;
}e[50010];
struct query
{
    int id,type;
    int pos,z;
}q[100010];

bool cmp(query a,query b)
{
    return a.pos<b.pos;
}

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dfs1(int v)
{
    son[v]=-1;
    siz[v]=1;
    for(int i=first[v];i;i=e[i].next)
    {
        dfs1(e[i].v);
        if (son[v]==-1||siz[e[i].v]>siz[son[v]])
            son[v]=e[i].v;
        siz[v]+=siz[e[i].v];
    }
}

void dfs2(int v,int tp)
{
    top[v]=tp;
    pos[v]=++tim;
    if (son[v]!=-1) dfs2(son[v],tp);
    for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=son[v]) dfs2(e[i].v,e[i].v);
}

void pushdown(int no,int l,int r)
{
    if (p[no])
    {
        int mid=(l+r)>>1;
        p[no<<1]+=p[no],p[no<<1|1]+=p[no];
        seg[no<<1]+=(ll)(mid-l+1)*p[no];
        seg[no<<1|1]+=(ll)(r-mid)*p[no];
        p[no]=0;
    }
}

void pushup(int no)
{
    seg[no]=seg[no<<1]+seg[no<<1|1];
}

void segmodify(int no,int l,int r,int s,int t)
{
    if (l>=s&&r<=t)
    {
        seg[no]+=(ll)(r-l+1);
        p[no]++;
        return;
    }
    int mid=(l+r)>>1;
    pushdown(no,l,r);
    if (s<=mid) segmodify(no<<1,l,mid,s,t);
    if (t>mid) segmodify(no<<1|1,mid+1,r,s,t);
    pushup(no);
}

ll segquery(int no,int l,int r,int s,int t)
{
    if (l>=s&&r<=t) return seg[no];
    int mid=(l+r)>>1;
    ll sum=0;
    pushdown(no,l,r);
    if (s<=mid) sum+=segquery(no<<1,l,mid,s,t);
    if (t>mid) sum+=segquery(no<<1|1,mid+1,r,s,t);
    return sum;
}

void modify(int x)
{
    while(x!=-1)
    {
        segmodify(1,1,n,pos[top[x]],pos[x]);
        x=fa[top[x]];
    }
}

ll query(int x)
{
    ll sum=0;
    while(x!=-1)
    {
        sum+=segquery(1,1,n,pos[top[x]],pos[x]);
        x=fa[top[x]];
    }
    return sum;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x;
        scanf("%d",&x);
        insert(x,i);
        fa[i]=x;
    }
    fa[0]=-1;
    dfs1(0);
    dfs2(0,0);

    tot=0;
    for(int i=1;i<=m;i++)
    {
        int l,r,z;
        scanf("%d%d%d",&l,&r,&z);
        q[++tot].id=i,q[tot].type=0,q[tot].pos=l-1,q[tot].z=z;
        q[++tot].id=i,q[tot].type=1,q[tot].pos=r,q[tot].z=z;
    }
    sort(q+1,q+tot+1,cmp);

    int last=1;
    while(q[last].pos==-1) last++;
    for(int i=0;i<n;i++)
    {
        modify(i);
        while(last<=tot&&q[last].pos==i)
        {
            if (q[last].type) ans[q[last].id]+=query(q[last].z);
            else ans[q[last].id]-=query(q[last].z);
            last++;
        }
    }

    for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]%201314);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值