【GZOI2019】旧词

题目链接:https://www.luogu.com.cn/problem/P5305

题目大意:给定一棵有根树,已知常数 \(k\), 对于 \(Q\) 个询问 \(x, y\)\(\sum\limits_{i \leq x} {depth(Lca(i, z))^k}\)

solution

\(k = 1\) 时, 显然就是原题【LN2014】LCA

\(k \ne 1\) , 则可以进一步推广上一题的算法 , 对于每次修改点到根的权值分别加 1 其实就是对 \(depth\)的一种差分\((depth(i) - depth(father(i)) = 1)\) , 因此从根到节点路径上的权值之和即为节点深度

现在依然可以将 \(depth^k\) 进行差分, 则若点 \(i\) 的深度为\(dep_i\) , 则 \(i\) 的权值每次应该增加 \({dep_i}^k - (dep_i - 1)^k\) , 最后从根节点到节点的路径上的权值之和即为 \({dep_i}^k\) , 可以先处理差分权值在\(dfs\)序区间上的前缀和 , 然后用树剖 + 线段树 , 离线进行修改与维护 , 具体做法同【LN2014】LCA

时间复杂度: \(O(nlog^2n)\)

code

#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
	int RR = 1; FF = 0; char CH = getchar();
	for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
	for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
	FF *= RR;
}
inline void file(string str) {
	freopen((str + ".in").c_str(), "r", stdin);
	freopen((str + ".out").c_str(), "w", stdout);
}
#define mod 998244353
#define int long long
const int N = 1e5 + 10;
int now, fst[N], nxt[N], num[N], dep[N], n, q, k, sum[N];
int p = 1, si, fa[N], tp[N], rev[N], sg[N], size[N], son[N], ans[N];
void add(int u, int v) {
	nxt[++now] = fst[u], fst[u] = now, num[now] = v;
	nxt[++now] = fst[v], fst[v] = now, num[now] = u;
}
struct Segment_tree{
	int val, tag;
}xds[N << 2];
struct Que{
	int id, pi, qi;
	friend bool operator < (Que ai, Que bi) {
		return ai.pi < bi.pi;
	}
}qi[N];
void push_up(int pos) {
	xds[pos].val = (xds[pos << 1].val + xds[pos << 1 | 1].val) % mod;
}
void Add(int pos, int l, int r, int k) {
	xds[pos].val = (xds[pos].val + k * ((sum[r] - sum[l - 1] + mod) % mod) % mod) % mod;
	xds[pos].tag = (xds[pos].tag + k) % mod;
}
void push_down(int pos, int l, int r) {
	if(xds[pos].tag == 0) return;
	int mid = (l + r) >> 1;
	Add(pos << 1, l, mid, xds[pos].tag);
	Add(pos << 1 | 1, mid + 1, r, xds[pos].tag);
	xds[pos].tag = 0;
}
void modify(int pos, int l, int r, int ll, int rr) {
	if(l >= ll && r <= rr) {
		Add(pos, l, r, 1);
		return;
	}
	push_down(pos, l, r);
	int mid = (l + r) >> 1;
	if(mid >= ll) modify(pos << 1, l, mid, ll, rr);
	if(mid < rr) modify(pos << 1 | 1, mid + 1, r, ll, rr);
	push_up(pos);
}
int query(int pos, int l, int r, int ll, int rr) {
	if(l >= ll && r <= rr) return xds[pos].val;
	push_down(pos, l, r);
	int mid = (l + r) >> 1, res = 0;
	if(mid >= ll) res = (res + query(pos << 1, l, mid, ll, rr)) % mod;
	if(mid < rr) res = (res + query(pos << 1 | 1, mid + 1, r, ll, rr)) % mod;
	return res;
}
void fst_dfs(int xi) {
	size[xi] = 1, dep[xi] = dep[fa[xi]] + 1;
	for(int i = fst[xi]; i; i = nxt[i])
		if(num[i] != fa[xi]) {
			fst_dfs(num[i]);
			size[xi] += size[num[i]];
			if(size[num[i]] > size[son[xi]])
				son[xi] = num[i];
		}
}
void nxt_dfs(int xi) {
	if(son[xi]) {
		sg[++si] = son[xi], rev[son[xi]] = si;
		tp[son[xi]] = tp[xi], nxt_dfs(son[xi]);
	}
	for(int i = fst[xi]; i; i = nxt[i])
		if(num[i] != son[xi] && num[i] != fa[xi]) {
			sg[++si] = num[i], rev[num[i]] = si;
			tp[num[i]] = num[i], nxt_dfs(num[i]);
		}
}
int Qpow(int ai, int ki) {
	if(ki <= 1) return ki ? ai % mod : 1;
	int hi = Qpow(ai, ki / 2); hi = hi * hi % mod;
	return ki & 1 ? hi * ai % mod : hi;
}
void modify_list(int xi) {
	while(xi) {
		modify(1, 1, n, rev[tp[xi]], rev[xi]);
		xi = fa[tp[xi]];
	}
}
int query_list(int xi) {
	int res = 0;
	while(xi) {
		res = (res + query(1, 1, n, rev[tp[xi]], rev[xi])) % mod;
		xi = fa[tp[xi]];
	}
	return res;
}
signed main() {
	//file("");	
	read(n), read(q), read(k);
	for(int i = 2; i <= n; i++)
		read(fa[i]), add(fa[i], i);
	si = tp[1] = rev[1] = sg[1] = 1;
	fst_dfs(1), nxt_dfs(1);
	for(int i = 1; i <= n; i++)
		sum[i] = ((sum[i - 1] + Qpow(dep[sg[i]], k)) % mod - Qpow(dep[sg[i]] - 1, k) + mod) % mod;
	for(int i = 1; i <= q; i++)
		read(qi[i].pi), read(qi[i].qi), qi[i].id = i;
	sort(qi + 1, qi + q + 1);
	//cout << query(1, 1, n, rev[2], rev[2]) << endl;
	for(int i = 1; i <= n; i++) {
		modify_list(i);
		//cout << query(1, 1, n, rev[3], rev[3]) << endl;
		while(qi[p].pi == i)
			ans[qi[p].id] = query_list(qi[p].qi), p++;
	}
	for(int i = 1; i <= q; i++) cout << ans[i] << endl;
	return 0;
}
posted @ 2020-01-28 20:54  MagicDuck  阅读(151)  评论(0编辑  收藏  举报