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;
    }
  • 相关阅读:
    HDU 1863 畅通工程(Kruskal)
    HDU 1879 继续畅通工程(Kruskra)
    HDU 1102 Constructing Roads(Kruskal)
    POJ 3150 Cellular Automaton(矩阵快速幂)
    POJ 3070 Fibonacci(矩阵快速幂)
    ZOJ 1648 Circuit Board(计算几何)
    ZOJ 3498 Javabeans
    ZOJ 3490 String Successor(模拟)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
  • 原文地址:https://www.cnblogs.com/lsy263/p/11487464.html
Copyright © 2011-2022 走看看