zoukankan      html  css  js  c++  java
  • [树上差分&树链剖分] [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
    ",q[i].ans);
        return 0;
    }
  • 相关阅读:
    【TopCoder 11469】—Nim(Fwt)
    【BZOJ4228】—Tibbar的后花园(生成函数+NTT)
    【BZOJ4228】—Tibbar的后花园(生成函数+NTT)
    多测师讲解接口测试 _postman(下)_高级讲师肖sir
    多测师讲解接口测试 _fiddler无法打开浏览器_高级讲师肖sir
    多测师讲解接口测试 _postman(上)_高级讲师肖sir
    多测师讲解接口测试 _HTTP常见的状态码归纳_高级讲师肖sir
    多测师讲解接口测试_F12中network里headers各项属性的含义——高级讲师肖sir
    SVN服务的模式和多种访问方式 多种访问原理图解与优缺点
    svn 目录
  • 原文地址:https://www.cnblogs.com/lsy263/p/11487464.html
Copyright © 2011-2022 走看看