[LuoguP5305][GXOI/GZOI2019]旧词 (树链剖分)

Posted birchtree

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LuoguP5305][GXOI/GZOI2019]旧词 (树链剖分)相关的知识,希望对你有一定的参考价值。

[GXOI/GZOI2019]旧词 (树链剖分)

题面

给定一棵 (n)个点的有根树,节点标号 ([1,n]),1号节点为根。
给定常数(k)
给定(Q)个询问,每次询问给定(x,y),求:(sum_{i=1}^x mathrm{deep}(mathrm{lca}(i,y)) mod 998244353)

分析

此题为[BZOJ3626] [LNOI2014]LCA(树链剖分)的加强版。

考虑原来的做法(k=1):我们把i到根的路径上所有点+1,y到根路径上的权值和就是lca深度。如果有多个点i,那么权值和就是深度之和。离线再线段树维护即可。

如果把1改成k,类比原来把(deep[i])拆成(deep[i])个1,我们可以把(deep[i]^k)拆成(deep[i]^{k}-(deep[i]-1)^k,(deep[i]-1)^k-(deep[i]-2)^k dots)等.这样问题就变成了用线段树维护一个序列(s)支持:

  1. (i in [l,r]),(s_i)加上定值(val[i]=deep[i]^k-(deep[i]-1)^k)
  2. ([l,r])的区间和

每个线段树区间维护一个变量delta记录区间内val之和,可以在建树的时候求出。

然后用一个懒标记addm记录每个区间被整体增加的次数,下推的时候加上addm*对应的val之和

 代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 200000
#define mod 998244353
using namespace std;
typedef long long ll;
inline void qread(int &x) {
    x=0;
    int sign=1;
    char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') sign=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    x=x*sign;
}
inline void qprint(ll x) {
    if(x<0) {
        putchar('-');
        qprint(-x);
    } else if(x==0) {
        putchar('0');
        return;
    } else {
        if(x>=10) qprint(x/10);
        putchar('0'+x%10);
    }
}

inline ll fast_pow(ll x,ll k) {
    ll ans=1;
    while(k) {
        if(k&1) ans=ans*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return ans;
}

int n,m,k;
struct edge {
    int from;
    int to;
    int next;
} E[maxn*2+5];
int esz=1;
int head[maxn+5];
void add_edge(int u,int v) {
    esz++;
    E[esz].from=u;
    E[esz].to=v;
    E[esz].next=head[u];
    head[u]=esz;
}


int deep[maxn+5],sz[maxn+5],fa[maxn+5],son[maxn+5],top[maxn+5],dfn[maxn+5],hash_dfn[maxn+5];
void dfs1(int x,int f) {
    fa[x]=f;
    sz[x]=1;
    deep[x]=deep[f]+1;
    for(int i=head[x]; i; i=E[i].next) {
        int y=E[i].to;
        if(y!=f) {
            dfs1(y,x);
            sz[x]+=sz[y];
            if(sz[son[x]]<sz[y]) son[x]=y;
        }
    }
}
int tim;
void dfs2(int x,int t) {
    dfn[x]=++tim;
    hash_dfn[dfn[x]]=x;
    top[x]=t;
    if(son[x]) dfs2(son[x],t);
    for(int i=head[x]; i; i=E[i].next) {
        int y=E[i].to;
        if(y!=fa[x]&&y!=son[x]) {
            dfs2(y,y);
        }
    }
}
int lca(int x,int y) {
    while(top[x]!=top[y]) {
        if(deep[top[x]]>deep[top[y]]) x=fa[top[x]];
        else y=fa[top[y]];
    }
    if(deep[x]<deep[y]) return x;
    else return y;
}





struct query {
    int x;
    int y;
    ll ans;
    friend bool operator < (query p,query q) {
        return p.x<q.x;
    }
} q[maxn+5];
vector<int>id[maxn+5];
ll val[maxn+5];//预处理每个点的贡献
struct segment_tree {
    struct node {
        int l;
        int r;
        ll sum;
        ll delta;//每次计算要加上的贡献,等于sum(val[i]) (l<=i<=r) 
        ll addm;//记录每个区间的贡献被计算的次数 
        int len() {
            return r-l+1;
        }
    } tree[maxn*4+5];
    void push_up(int pos) {
        tree[pos].sum=(tree[pos<<1].sum+tree[pos<<1|1].sum)%mod;
        tree[pos].delta=(tree[pos<<1].delta+tree[pos<<1|1].delta)%mod;
    }
    void build(int l,int r,ll *a,int pos) {
        tree[pos].l=l;
        tree[pos].r=r;
        if(l==r){
            tree[pos].delta=a[hash_dfn[l]];
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,a,pos<<1);
        build(mid+1,r,a,pos<<1|1);
        push_up(pos);
    }
    void add_tag(int x,int mark) {
        tree[x].sum+=mark*tree[x].delta%mod;
        tree[x].sum%=mod;
        tree[x].addm+=mark;
        tree[x].addm%=mod;
    }
    void push_down(int x) {
        if(tree[x].addm) {
            add_tag(x<<1,tree[x].addm);
            add_tag(x<<1|1,tree[x].addm);
            tree[x].addm=0;
        }
    }
    void update(int L,int R,int val,int pos) {
        if(L<=tree[pos].l&&R>=tree[pos].r) {
            add_tag(pos,val);
            return;
        }
        push_down(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1;
        if(L<=mid) update(L,R,val,pos<<1);
        if(R>mid) update(L,R,val,pos<<1|1);
        push_up(pos);
    }
    ll query(int L,int R,int pos) {
        if(L<=tree[pos].l&&R>=tree[pos].r) {
            return tree[pos].sum;
        }
        push_down(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1;
        ll ans=0;
        if(L<=mid) ans+=query(L,R,pos<<1);
        if(R>mid) ans+=query(L,R,pos<<1|1);
        return ans;
    }
} T;

void update(int x,int y) {
    int tx=top[x],ty=top[y];
    while(tx!=ty) {
        if(deep[tx]<deep[ty]) {
            swap(x,y);
            swap(tx,ty);
        }
        T.update(dfn[tx],dfn[x],1,1);
        x=fa[tx];
        tx=top[x];
    }
    if(deep[x]>deep[y]) swap(x,y);
    T.update(dfn[x],dfn[y],1,1);
}
ll query(int x,int y) {
    ll ans=0;
    int tx=top[x],ty=top[y];
    while(tx!=ty) {
        if(deep[tx]<deep[ty]) {
            swap(x,y);
            swap(tx,ty);
        }
        ans+=T.query(dfn[tx],dfn[x],1);
        x=fa[tx];
        tx=top[x];
    }
    if(deep[x]>deep[y]) swap(x,y);
    ans+=T.query(dfn[x],dfn[y],1);
    return ans;
}


int main() {
    int f;
    qread(n);
    qread(m);
    qread(k);
    for(int i=2; i<=n; i++) {
        qread(f);
        add_edge(i,f);
        add_edge(f,i);
    }
    dfs1(1,0);
    dfs2(1,1);
    for(int i=1; i<=m; i++) {
        qread(q[i].x);
        qread(q[i].y);
        id[q[i].x].push_back(i);
    }
    for(int i=1;i<=n;i++){
        val[i]=(fast_pow(deep[i],k)-fast_pow(deep[i]-1,k)+mod)%mod;
    }
    T.build(1,n,val,1);
    for(int i=1; i<=n; i++) {
        update(1,i);
        for(int j=0; j<(int)id[i].size(); j++) {
            int num=id[i][j];
            q[num].ans=query(1,q[num].y)%mod;
        }
    }
    for(int i=1; i<=m; i++) {
        qprint(q[i].ans);
        putchar('
');
    }
}

/*
5 5 1
1
4
1
2
4 3
5 4
2 5
1 2
3 2
*/

以上是关于[LuoguP5305][GXOI/GZOI2019]旧词 (树链剖分)的主要内容,如果未能解决你的问题,请参考以下文章

[GXOI/GZOI2019旅行者]

[GXOI/GZOI2019]与或和

「GXOI / GZOI2019」旧词

[GXOI/GZOI2019]与或和[单调栈]

GXOI/GZOI2019 旅行者

题解-GXOI/GZOI2019 特技飞行