【GXOI / GZOI2019】【树链剖分】【线段树】旧词

【题目描述】
浮生有梦三千场
穷尽千里诗酒荒
徒把理想倾倒
不如早还乡

温一壶风尘的酒
独饮往事迢迢
举杯轻思量
泪如潮青丝留他方

——乌糟兽/愚青《旧词》

你已经解决了五个问题,不妨在这大树之下,吟唱旧词一首抒怀。最后的问题就是关于这棵树的,它的描述很简单。给定一棵 n 个点的有根树,节点标号 1∼n,1号节点为根。给定常数 k。给定 Q 个询问,每次询问给定 x,y。求:
∑ i ≤ x d e p t h ( l c a ( i , y ) ) k ∑_{i≤x}depth(lca(i,y))^k ixdepth(lca(i,y))k
l c a ( x , y ) lca(x,y) lca(x,y) 表示节点 x 与节点 y 在有根树上的最近公共祖先。
d e p t h ( x ) depth(x) depth(x)表示节点x 的深度,根节点的深度为 1。
由于答案可能很大,你只需要输出答案模998244353 的结果。

【思路】

这题就是LNOI2014LCA玩剩下的套路。我们先讨论LNOI2014LCA中k=1的情况。我们其实并不需要求出LCA是谁来确定它的深度,还有另一种拓展性更强的方法。在这里插入图片描述
如图,我们要求LCA(x,y)的深度。那么我们先将x到根节点的路径上的点的权值全部加1,然后我们查询y到根节点的路径上所有点的权值之和就是LCA的深度。不要小看这样的方法,它对于多个x的情况也是成立的。即我们把[l,r]的点到根节点的路径上的权值全部加1,那么查询y到根节点的路径的和,查出的就是 ∑ i = l r d e p t h ( l c a ( i , y ) ) \sum_{i=l}^rdepth(lca(i,y)) i=lrdepth(lca(i,y))。所以我们只需要依次将每个点进行链加操作,每次操作后通过链查询回答有关询问,对于询问[l,r],只需拆成 q u e r y ( r ) − q u e r y ( l − 1 ) query(r)-query(l-1) query(r)query(l1)即可。时间复杂度O( n log ⁡ 2 n n\log^2 n nlog2n)。对于k不等于1的情况也是类似的,我们刚刚的方法的本质就是把深度进行了差分,对于这道题我们把 d e p k dep^k depk也进行差分即可,即赋予每个节点u一个权值 v a l = d e p [ u ] k − ( d e p [ u ] − 1 ) k val=dep[u]^k-(dep[u]-1)^k val=dep[u]k(dep[u]1)k,每次将到根节点路径上每个点加上它自己的权值即可,由于这个操作可以打lazy标记,所以线段树时间复杂度没有问题。
代码:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define re register
using namespace std;
const int N=5e5+5;
const int mod=998244353;
int n,m,a,b,c,k;
inline int red(){
    int data=0;int w=1; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return w==1?data:-data;
}
int val[N],dep[N],fa[N],son[N],siz[N],rev[N],seg[N],top[N],tot=0;vector<int>g[N];
inline int add(const int&a,const int&b){return (a+b)>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return (a-b)<0?a-b+mod:a-b;}
inline int mul(const int&a,const int&b){return 1ll*a*b%mod;}
inline int ksm(int a,int b){
	int ret=1;
	while(b){if(b&1)ret=mul(ret,a);a=mul(a,a);b>>=1;}
	return ret;
}
namespace sgt{
	#define lc (p<<1)
	#define rc (p<<1|1)
	int sum[N<<2|1],ans[N<<2|1],laz[N<<2|1];
	inline void pushup(const int &p){ans[p]=add(ans[lc],ans[rc]);}
	inline void pushnow(const int&p,const int&v){laz[p]+=v;ans[p]=add(ans[p],mul(v,sum[p]));}
	inline void pushdown(const int&p){if(laz[p])pushnow(lc,laz[p]),pushnow(rc,laz[p]),laz[p]=0;}
	inline void build(const int&p,const int&l,const int&r){
		if(l==r)return(void)(sum[p]=val[dep[rev[l]]]);
		int mid=(l+r)>>1;
		build(lc,l,mid);build(rc,mid+1,r);
		sum[p]=add(sum[lc],sum[rc]);
	}
	inline void change(const int&p,const int&l,const int&r,const int&ql,const int&qr,const int&v){
		if(ql<=l&&qr>=r)return pushnow(p,v);
		int mid=(l+r)>>1;pushdown(p);
		if(ql<=mid)change(lc,l,mid,ql,qr,v);
		if(qr>mid)change(rc,mid+1,r,ql,qr,v);
		pushup(p);
	}
	inline int query(const int&p,const int&l,const int&r,const int&ql,const int&qr){
		if(ql<=l&&qr>=r)return ans[p];
		int mid=(l+r)>>1,res=0;pushdown(p);
		if(ql<=mid)res=add(res,query(lc,l,mid,ql,qr));
		if(qr>mid)res=add(res,query(rc,mid+1,r,ql,qr));
		return res;
	}
}
inline void dfs1(int u){siz[u]=1;int v;
	for(int re i=g[u].size()-1;~i;--i){
		dep[v=g[u][i]]=dep[u]+1;dfs1(v);siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
inline void dfs2(int u){int v;
	if(son[u]){
		seg[son[u]]=++tot;rev[tot]=son[u];
		top[son[u]]=top[u];dfs2(son[u]);
	}for(int re i=g[u].size()-1;~i;--i)
		if(!top[v=g[u][i]])
			seg[v]=++tot,rev[tot]=v,top[v]=v,dfs2(v);
}
inline int qroad(int p){
	int fx=top[p],res=0;
	while(p)res=add(res,sgt::query(1,1,n,seg[fx],seg[p])),p=fa[fx],fx=top[p];
	return res;
}
inline void croad(int p){
	int fx=top[p];
	while(p)sgt::change(1,1,n,seg[fx],seg[p],1),p=fa[fx],fx=top[p];
}
struct node{
	int r,y,id;
	friend inline bool operator<(const node&a,const node&b){return a.r<b.r;}
}q[N];
int ans[N];
int main(){n=red();m=red();k=red();
	for(int re i=1;i<=n;i++)val[i]=ksm(i,k);
	for(int re i=n;i;--i)val[i]=dec(val[i],val[i-1]);
	for(int re i=2;i<=n;++i)g[fa[i]=red()].push_back(i);
	dfs1(dep[1]=1);top[1]=seg[1]=tot=1;
	dfs2(rev[tot]=1);sgt::build(1,1,n);
	for(int re i=1;i<=m;i++)q[i]=(node){red(),red(),i};
	sort(q+1,q+m+1);int r=0;
	for(int re i=1;i<=m;i++){
		while(r<q[i].r)croad(++r);
		ans[q[i].id]=qroad(q[i].y);
	}for(int re i=1;i<=m;i++)
		printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值