[GX/GZOI2019]旧词(树上差分+树剖+线段树)

考虑k=1的做法:这是一道原题,我还写过题解,其实挺水的,但当时我菜还是看题解的:https://www.cnblogs.com/hfctf0210/p/10187947.html。其实就是树上差分后值为1。

考虑k>1的做法:其实可以再次树上差分,给每个点i赋值v[i]=dep[i]k-dep[i-1]k,然后还是和原来一样开一棵线段树,记录一个val[rt]表示当前节点内区间v值的和,以及sum[rt]表示区间值。修改时打标记,只需要将sum[rt]+=v*val[rt],lazy[rt]+=v即可。树剖一下即可。

#include<bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef pair<int,int>pii;
const int N=51000,mod=998244353;
int n,Q,k,cnt,dep[N],fa[N],pw[N],sz[N],son[N],top[N],id[N],dfx[N];
int ans[N],sum[N<<2],val[N<<2],lazy[N<<2];
vector<int>G[N];
vector<pii>vec[N];
int qpow(int a,int b)
{
    int ret=1;
    while(b)
    {
        if(b&1)ret=1ll*ret*a%mod;
        a=1ll*a*a%mod,b>>=1;
    }
    return ret;
}
void dfs(int u)
{
    dep[u]=dep[fa[u]]+1,sz[u]=1;
    for(int i=0;i<G[u].size();i++)
    if(G[u][i]!=fa[u])
    {
        dfs(G[u][i]),sz[u]+=sz[G[u][i]];
        if(sz[G[u][i]]>sz[son[u]])son[u]=G[u][i];
    }
}
void dfs2(int u,int tp)
{
    top[u]=tp,id[u]=++cnt,dfx[cnt]=u;
    if(son[u])dfs2(son[u],tp);
    for(int i=0;i<G[u].size();i++)
    if(G[u][i]!=fa[u]&&G[u][i]!=son[u])dfs2(G[u][i],G[u][i]);
}
void build(int l,int r,int rt)
{
    if(l==r){val[rt]=pw[dep[dfx[l]]];return;}
    int mid=l+r>>1;
    build(lson),build(rson);
    val[rt]=(val[rt<<1]+val[rt<<1|1])%mod;
}
void pushdown(int rt)
{
    if(!lazy[rt])return;
    int v=lazy[rt];lazy[rt]=0;
    lazy[rt<<1]=(lazy[rt<<1]+v)%mod,sum[rt<<1]=(sum[rt<<1]+1ll*v*val[rt<<1])%mod;
    lazy[rt<<1|1]=(lazy[rt<<1|1]+v)%mod,sum[rt<<1|1]=(sum[rt<<1|1]+1ll*v*val[rt<<1|1])%mod;
}
void update(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){lazy[rt]++,sum[rt]=(sum[rt]+val[rt])%mod;return;}
    pushdown(rt);
    int mid=l+r>>1;
    if(L<=mid)update(L,R,lson);
    if(R>mid)update(L,R,rson);
    sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod;
}
void Update(int u)
{
    while(top[u]!=1)update(id[top[u]],id[u],1,n,1),u=fa[top[u]];
    update(1,id[u],1,n,1);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)return sum[rt];
    pushdown(rt);
    int mid=l+r>>1,ret=0;
    if(L<=mid)ret=(ret+query(L,R,lson))%mod;
    if(R>mid)ret=(ret+query(L,R,rson))%mod;
    return ret;
}
int Query(int u)
{
    int ret=0;
    while(top[u]!=1)ret=(ret+query(id[top[u]],id[u],1,n,1))%mod,u=fa[top[u]];
    ret=(ret+query(1,id[u],1,n,1))%mod;
    return ret;
}
int main()
{
    scanf("%d%d%d",&n,&Q,&k);
    for(int i=1;i<=n;i++)pw[i]=(qpow(i,k)-qpow(i-1,k)+mod)%mod;
    for(int i=2;i<=n;i++)scanf("%d",&fa[i]),G[fa[i]].push_back(i);
    dfs(1),dfs2(1,1);
    build(1,n,1);
    for(int i=1,x,y;i<=Q;i++)scanf("%d%d",&x,&y),vec[x].push_back(pii(y,i));
    for(int i=1;i<=n;i++)
    {
        Update(i);
        for(int j=0;j<vec[i].size();j++)ans[vec[i][j].second]=Query(vec[i][j].first);
    }
    for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
}

原文地址:https://www.cnblogs.com/hfctf0210/p/10859304.html

时间: 2024-11-09 03:00:44

[GX/GZOI2019]旧词(树上差分+树剖+线段树)的相关文章

POJ3417Network(LCA+树上查分||树剖+线段树)

Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN's business rival, intents to attack the network of SN. More unfortunately, the origina

BZOJ4034 树上操作(树剖 线段树大模板)

BZOJ4034 long long 是大坑点 貌似long long 跟int 乘起来会搞事情?... A了这题线段树和树剖的基础OK 嘛 重点过掉的还是线段树区间更新的lazy tag吧 #include<cstdio> #include<cstring> #define N 100001 using namespace std; struct ed{ int nxt,to; }e[N*2]; int ne=0,head[N]; long long int w0[N]; str

poj3728The merchant树剖+线段树

如果直接在一条直线上,那么就建线段树 考虑每一个区间维护最小值和最大值和答案,就符合了合并的条件,一个log轻松做 那么在树上只要套一个树剖就搞定了,多一个log也不是问题 注意考虑在树上的话每一条链都有可能是正着被用和反着被用,所以存两个答案 所以维护信息只需要一个merge和一个reverse 代码如下: 1 #include <cstdio> 2 #include <iostream> 3 #define mid (l+r>>1) 4 using namespac

bzoj 5210: 最大连通子块和【动态dp+树剖+线段树+堆】

参考:https://www.cnblogs.com/CQzhangyu/p/8632904.html 要开longlong的 首先看dp,设f[u]为必选u点的子树内最大联通块,p[u]为不一定选u的子树内最大联通块,转移很显然就是f[u]=max(Σf[v],0),p[u]=max(max(p[v]),f[u]) 然后看动态的部分,设g是不算重儿子的f,然后每条链上的真实f值要用一棵线段树维护g来得到,具体形式是f[u]=max(g[v]+f[hs[v]],0),是一个最长连续子序列的形式,

[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到根路径上的权值

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

UVALive 7148 LRIP【树分治+线段树】

题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以x为第一个数字的不下降子序列中第i个数的最小值,MX[i]表示以x为第一个数字的不上升子序列中第i个数的最大值.如果当前子树有一个以x为首的不下降序列,那么我们就需要在之前处理的子树中找一条以x为首的满足约束条件不上升序列,可以用线段树来查询.同时每做完一颗子树的时候,用MN,MX对线段树进行更新.

区间树和线段树

注意:区间树和线段树不一样哦,线段树是一种特殊的区间树. 区间树: 区间树是在红黑树基础上进行扩展得到的支持以区间为元素的动态集合的操作,其中每个节点的关键值是区间的左端点.通过建立这种特定的结构,可是使区间的元素的查找和插入都可以在O(lgn)的时间内完成.相比于基础的红黑树数据结构,增加了一个max[x],即以x为根的子树中所有区间的断点的最大值.逻辑结构如下所示: 区间树具有和红黑树一样的性质,并且区间树的基础操作和红黑树的基础操作一样.(具体红黑树的操作,参见http://blog.cs

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin