Live2D

[树上差分&树链剖分] [GXOI/GZOI2019]旧词

luoguP5305 [GXOI/GZOI2019]旧词

黑题?看起来很难的样子。(所以写篇题解纪念一下)

加强版: luoguP4211  [LNOI2015]LCA


我们还是看原版吧。

首先看到这个奇怪的查询,我们首先想到了莫队。

(莫队:把区间询问按区间位置排好再处理,离线。)

 

每次y会变化,所以不能预处理出LCA。

莫队后x会递增,考虑每次在上次答案的基础上加上新增x的贡献。

 

但是因为y不同,所以并不能直接加入答案。

所以我们要找到一种 与y没有关系 的求解方法。

 

树上差分。

 

简单地说,就是使得节点x到根节点root的路径上的权值和等于结点x的贡献,显然这个权值是我们加上的。

怎么实现呢?

 

有一个数组dpk[ i ] ,表示i号节点的k次方。

现在我们要一个数组,使得从头加到i的和等于dpk[i]。

想到了什么?

没错,差分数组。

(差分数组diff[i] = a[i]-a[i-1] ,差分数组的前缀和等于原数组。)

 

于是,这题的基本思路就出来了。

对于每个x(指已经经过的节点),把root ~ x的路径上的节点都搞成“差分”,也就是若 u 是路径上的一个节点,则有 val[u] += dpk[u] - dpk[f[u]]。

这样,从root ~ x的和就等于dpk[x]了。(而且也可以求出路径上的任一个点的dpk,这句话很重要。)

 

回到y。

怎么求所有LCA的dpk和呢?

考虑只有一个节点x,也就是普通的LCA。

显然x与y的LCA,是root~x和root~y两条路径上最深的一个交点,于是我们又发现,只要把root~y上的和加起来,得到的就是两路径重合部分的最深一个点的dpk,这个点是谁?是LCA。

 

同理,在有多个节点的情况下也适用,因为每次加的值不会干扰。

 

root~x使用树剖处理。

 

上代码,

开5e4+5的数组RE了,于是我开了5e5+5。

因为用int型,乘法会溢出,所以写了个快速乘,longlong直接乘法。

#include<iostream>
#include<cstdio>
#include<algorithm>
typedef long long LL;
const int Mod=998244353,N=5e5+5;
int mul(int a,int b){
    int base=0;
    while(b){
        if(b&1)(base+=a)%=Mod;
        (a+=a)%=Mod;b>>=1;
    }return base;
}
int powe(int a,LL b){
    int base=1;
    a%=Mod;
    while(b){
        if(b&1)(base=mul(base,a))%=Mod;
        (a=mul(a,a))%=Mod;b>>=1;
    }return base;
}
struct EDGE{
    int nt,v;
}e[N];
int tot=0,hd[N],val[N];
void add_edge(int u,int v){
    e[++tot]=(EDGE){hd[u],v};
    hd[u]=tot;
}
int n,Q,dpk[N];
LL k;
int f[N],dep[N],siz[N],son[N];
int ontree_pos[N],real_pos[N],top[N],tag[N],tpos=0;
namespace Tree {
    void dfs1(int u,int deep){
        dep[u]=deep;
        dpk[u]=powe(deep,k);
        siz[u]=1;
        for(int i=hd[u];i;i=e[i].nt){
            int v=e[i].v;
            dfs1(v,deep+1);
            siz[u]+=siz[v];
            if(siz[v]>siz[son[u]])
                son[u]=v;
        }
    }
    void dfs2(int u,int topn){
        ontree_pos[u]=++tpos;
        real_pos[tpos]=u;
        top[u]=topn;
        if(!son[u])return;
        dfs2(son[u],topn);
        for(int i=hd[u];i;i=e[i].nt){
            int v=e[i].v;
            if(v!=son[u])dfs2(v,v);
        }
    }
    /* */
    
    void build(int rt,int l,int r){
        if(l==r){
            int u=ontree_pos[l];
            (val[rt]=powe(dep[u],k)+Mod
            -powe(dep[f[u]],k))%=Mod;
        }
        int mid=(l+r)>> 1;
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
        
    }
    /* */
    #define p(u) ontree_pos[u]
    #define add(rt,l,r,t) \
     (val[rt]+=mul((dpk[r]+Mod-dpk[f[l]])%Mod,t))%=Mod;
    void pushdown(int rt,int l,int r){
        if(!tag[rt]||l==r)return;
        int LS=rt<<1,RS=rt<<1|1;
        int mid=(l+r)>>1;
        add(LS,real_pos[l],real_pos[mid],tag[rt]);
        add(RS,real_pos[mid+1],real_pos[r],tag[rt]);
        tag[LS]+=tag[rt];
        tag[RS]+=tag[rt];
        tag[rt]=0;
    }
    void update(int rt,int l,int r,int x,int y){
        if(l>=x&&r<=y){
            add(rt,real_pos[l],real_pos[r],1);
            ++tag[rt];
            return ;
        }
        pushdown(rt,l,r);
        int mid=(l+r)>>1;
        if(x<=mid)update(rt<<1,l,mid,x,y);
        if(y>mid)update(rt<<1|1,mid+1,r,x,y);
        val[rt]=(val[rt<<1]+val[rt<<1|1])%Mod;
        return;
    }
    int query(int rt,int l,int r,int x,int y){
        if(l>=x&&r<=y)return val[rt];
        pushdown(rt,l,r);
        int mid=(l+r)>>1,res=0;
        if(x<=mid)(res+=query(rt<<1,l,mid,x,y))%=Mod;
        if(y>mid)(res+=query(rt<<1|1,mid+1,r,x,y))%=Mod;
        return res;
    }
    
    void Add_link(int x){
        while(p(top[x])!=1){
            update(1,1,n,p(top[x]),p(x));
            x=f[top[x]];
        }update(1,1,n,1,p(x));
    }
    int Query_link(int x){
        int res=0;
        while(p(top[x])!=1){
            (res+=query(1,1,n,p(top[x]),p(x)))%=Mod;
            x=f[top[x]];
        }(res+=query(1,1,n,1,p(x)))%=Mod;
        return res;
    }
    #undef p
    #undef add
}


using namespace std;
using namespace Tree;
struct Que{
    int x,y,ans,pos;
}q[N];
bool cmp1(Que a,Que b){
    return a.x<b.x;
}
bool cmp2(Que a,Que b){
    return a.pos<b.pos;
}
int main(){
    scanf("%d%d%lld",&n,&Q,&k);
    for(int i=2;i<=n;++i){
        scanf("%d",&f[i]);
        add_edge(f[i],i);
    }
    for(int i=1;i<=Q;++i){
        scanf("%d%d",&q[i].x,&q[i].y);
        q[i].pos=i;
    }
    sort(q+1,q+1+Q,cmp1);
    dfs1(1,1);dfs2(1,1);
    int nowx=1;
    for(int i=1;i<=Q;++i){
        while(nowx<=q[i].x)
            Add_link(nowx),++nowx;
        q[i].ans=Query_link(q[i].y);
    }
    sort(q+1,q+1+Q,cmp2);
    for(int i=1;i<=Q;++i)
        printf("%d\n",q[i].ans);
    return 0;
}

 

posted @ 2019-09-08 17:24  lsy263  阅读(204)  评论(0编辑  收藏  举报