[GXOI/GZOI2019]旧词

Description

给定一棵 \(n\) 个点的有根树,节点标号 \(1 \sim n\)\(1\) 号节点为根。

给定常数 \(k\)
给定 \(Q\) 个询问,每次询问给定 \(x,y\)

求:
\[ \sum\limits_{i \le x} \text{depth}(\text{lca}(i,y))^k \]

\(\text{lca}(x,y)\) 表示节点 \(x\) 与节点 \(y\) 在有根树上的最近公共祖先。
\(\text{depth}(x)\) 表示节点 \(x\) 的深度,根节点的深度为 \(1\)
由于答案可能很大,你只需要输出答案模 \(998244353\) 的结果。

Solution

\(k=1\) 的时候就是 [LNOI2014]LCA.

\(k\not =1\) 的时候其实本质上是一样的,我们需要保证加入一个节点 \(x\) 的对 \(y\) 的贡献为 \(depth(lca(x,y))^k\) ,那么我们可以构造一个序列,把 \(x\) 到根路径上全部加上这段序列后查询 y 到根的路径和会新增 \(depth(lca(x,y))^k\),所以令这个序列为 \(A\),则 \(A_i=dep(i)^k-dep(i-1)^k\),由于路径深度是连续的,所以巧妙地差分掉了,其余的做法和上面那道题相似,不再赘述。

#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

using namespace std;

#define LL long long
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cout << #x << " = " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;

inline void proc_status()
{
    ifstream t("/proc/self/status");
    cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
template<class T> inline T read() 
{
    register char c;
    register T x(0), f(1);
    while (!isdigit(c = getchar())) if (c == '-') f = -1;
    while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
    return x * f;
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN=5e4;

#define mod (998244353)
inline void Mod(int& x) { x>=mod?x-=mod:0;}

LL qpow(LL a,LL b)
{
    LL ans(1);
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

int n,Q,k;
int ver[maxN<<1],nxt[maxN<<1],head[maxN+2],tot;
int A[maxN+2],dfn[maxN+2],son[maxN+2],size[maxN+2],dep[maxN+2],rev[maxN+2],dfst,top[maxN+2],fa[maxN+2];
int sum[maxN<<2],tag[maxN<<2];
void link(int u,int v)
{
    ver[++tot]=v,nxt[tot]=head[u],head[u]=tot;
}
void DFS1(int u,int f)
{
    fa[u]=f;
    dep[u]=dep[f]+1;
    size[u]=1;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=ver[i];
        if(v==f)continue;
        DFS1(v,u);
        size[u]+=size[v];
        if(size[son[u]]<size[v])son[u]=v;
    }
}
void DFS2(int u,int topf)
{
    dfn[u]=++dfst;
    rev[dfst]=u;
    top[u]=topf;
    if(son[u])DFS2(son[u],topf);
    for(int i=head[u];i;i=nxt[i])
    {
        int v=ver[i];
        if(v==son[u]||v==fa[u])continue;
        DFS2(v,v);
    }
}
void init()
{
    DFS1(1,0);
    DFS2(1,1);
    for(int i=1;i<=n;++i)
        A[i]=(A[i-1]+qpow(dep[rev[i]],k)-qpow(dep[rev[i]]-1,k)+mod)%mod;
}
void push(int x,int v,int l,int r)
{
    tag[x]+=v;
    sum[x]+=(LL)(A[r]-A[l-1]+mod)*v%mod;
    Mod(sum[x]);
}
void pushdown(int x,int l,int r)
{
    if(tag[x])
    {
        int mid((l+r)>>1);
        push(x<<1,tag[x],l,mid);
        push(x<<1|1,tag[x],mid+1,r);
        tag[x]=0;
    }
}
void add(int x,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
        return push(x,1,l,r);
    int mid=(l+r)>>1;
    pushdown(x,l,r);
    if(L<=mid)add(x<<1,l,mid,L,R);
    if(mid<R)add(x<<1|1,mid+1,r,L,R);
    sum[x]=sum[x<<1]+sum[x<<1|1],Mod(sum[x]);
}
int query(int x,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
        return sum[x];
    int mid=(l+r)>>1;
    pushdown(x,l,r);
    int ans(0);
    if(L<=mid)ans+=query(x<<1,l,mid,L,R);
    if(mid<R)ans+=query(x<<1|1,mid+1,r,L,R),Mod(ans);
    return ans;
}
vector<pair<int, int> >q[maxN+2];
int ans[maxN+2];
int main() 
{
#ifndef ONLINE_JUDGE
    freopen("xhc.in", "r", stdin);
    freopen("xhc.out", "w", stdout);
#endif
    n=read<int>(),Q=read<int>(),k=read<int>();
    for(int i=2;i<=n;++i)
    {
        int f(read<int>());
        link(f,i);
        link(i,f);
    }
    init();
    for(int i=1;i<=Q;++i)
    {
        int x(read<int>()),y(read<int>());
        q[x].push_back(MP(y,i));
    }
    for(int i=1;i<=n;++i)
    {
        int u=i;
        while(u)
        {
            add(1,1,n,dfn[top[u]],dfn[u]);
            u=fa[top[u]];
        }
        for(int j=0;j<SZ(q[i]);++j)
        {
            int u=q[i][j].first,id=q[i][j].second,res=0;
            while(u)
            {
                res+=query(1,1,n,dfn[top[u]],dfn[u]);
                Mod(res);
                u=fa[top[u]];
            }
            ans[id]=res;
        }
    }
    for(int i=1;i<=Q;++i)
        printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/cnyali-Tea/p/11567668.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值