[GXOI/GZOI2019] 旧词

前言

挺有意思,写出来一遍过挺爽的,于是水沝淼㵘一篇博客

题目

洛谷

LOJ

讲解

先看弱化版[LNOI2014]LCA

之前的思路是考虑LCA到根上每个点的贡献,用树链剖分维护即可

类似地,我们考虑先对询问按 \(x\) 从小到大排序

对于每个询问达到 \(x\) 时,我们对其到根的路径上加上权值

由于本题有一个 \(k\) 次方,所以我们不能再加 \(1\),而是应该加上\(depth^k(i)-depth^k(i-1)\)

这样我们如果求出 \(y\) 到根的路径上的点权和,即求出了答案

我们依然可以区间加一(懒标记),只是每个节点加的权值为上面的那个式子,可以预处理

代码

int qpow(int x,int y)
{
	int ret = 1;
	while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
	return ret;
}

int head[MAXN],tot;
struct edge
{
	int v,nxt;
}e[MAXN];
void Add_Edge(int x,int y)
{
	e[++tot].v = y;
	e[tot].nxt = head[x];
	head[x] = tot;
}

int d[MAXN],siz[MAXN],son[MAXN],f[MAXN];
void dfs1(int x)
{
	d[x] = d[f[x]] + 1;
	siz[x] = 1;
	for(int i = head[x]; i ;i = e[i].nxt)
	{
		dfs1(e[i].v);
		siz[x] += siz[e[i].v];
		if(siz[e[i].v] > siz[son[x]]) son[x] = e[i].v;
	}
}
int tp[MAXN],dfn[MAXN],dfntot,rdfn[MAXN];
void dfs2(int x,int t)
{
	dfn[x] = ++dfntot;
	rdfn[dfntot] = x;
	tp[x] = t;
	if(!son[x]) return;
	dfs2(son[x],t);
	for(int i = head[x]; i ;i = e[i].nxt)
		if(e[i].v != son[x])
			dfs2(e[i].v,e[i].v);
}

#define lc (x<<1)
#define rc (x<<1|1)
struct SegmentTree
{
	struct node
	{
		int pre,s,lz;
	}t[MAXN << 2];
	
	void down(int x)
	{
		if(!t[x].lz) return;
		t[lc].s = (t[lc].s + 1ll * t[lc].pre * t[x].lz) % MOD;
		t[rc].s = (t[rc].s + 1ll * t[rc].pre * t[x].lz) % MOD;
		t[lc].lz += t[x].lz; t[rc].lz += t[x].lz;
		t[x].lz = 0;
	}
	
	void up(int x)
	{
		t[x].s = (t[lc].s + t[rc].s) % MOD;
	}
	
	void Build(int x,int l,int r)
	{
		if(l == r)
		{
			t[x].pre = (qpow(d[rdfn[l]],k) - qpow(d[rdfn[l]]-1,k) + MOD) % MOD;
			return;
		}
		int mid = (l+r) >> 1;
		Build(lc,l,mid); Build(rc,mid+1,r);
		t[x].pre = (t[lc].pre + t[rc].pre) % MOD;
	}
	
	void Add(int x,int l,int r,int ql,int qr)
	{
		if(ql <= l && r <= qr)
		{
			t[x].lz++;
			t[x].s = (t[x].pre + t[x].s) % MOD;
			return;
		}
		down(x);
		int mid = (l+r) >> 1;
		if(ql <= mid) Add(lc,l,mid,ql,qr);
		if(mid+1 <= qr) Add(rc,mid+1,r,ql,qr);
		up(x);
	}
	
	int Query(int x,int l,int r,int ql,int qr)
	{
		if(ql <= l && r <= qr) return t[x].s;
		down(x);
		int mid = (l+r) >> 1,ret = 0;
		if(ql <= mid) ret += Query(lc,l,mid,ql,qr);
		if(mid+1 <= qr) ret += Query(rc,mid+1,r,ql,qr);
		return ret % MOD;
	}
}st;

void AddChain(int x) 
{
	while(x)
	{
		st.Add(1,1,n,dfn[tp[x]],dfn[x]);
		x = f[tp[x]];
	}
}
int QueryChain(int x)
{
	int ret = 0;
	while(x)
	{
		ret = (ret + st.Query(1,1,n,dfn[tp[x]],dfn[x])) % MOD;
		x = f[tp[x]];
	}
	return ret;
}

struct query
{
	int x,y,ID;
	bool operator < (const query &px)const{
		return x < px.x;
	}
}q[MAXN];

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); Q = Read(); k = Read();
	for(int i = 2;i <= n;++ i) Add_Edge(f[i] = Read(),i);
	for(int i = 1;i <= Q;++ i) q[i].x = Read(),q[i].y = Read(),q[i].ID = i;
	sort(q+1,q+Q+1);
	dfs1(1);
	dfs2(1,1);
	st.Build(1,1,n);
	int now = 1;
	for(int i = 1;i <= Q;++ i)
	{
		while(now <= q[i].x) AddChain(now++);
		ans[q[i].ID] = QueryChain(q[i].y);
	}
	for(int i = 1;i <= Q;++ i) Put(ans[i],'\n');
	return 0;
}
posted @ 2021-01-05 21:13  皮皮刘  阅读(53)  评论(0编辑  收藏  举报