do_while_true

一言(ヒトコト)

「题解」Codeforces 1578J Just Kingdom

从底向上考虑,如果当前节点 \(u\) 需要 \(x\) 块钱,它的兄弟(拥有相同父亲的其他节点)为 \(v\),那么它的父亲需要 \(x+\sum \min(size_v,x)\) 块钱才能保证 \(u\) 拿到 \(x\) 块钱,其中 \(size_v\)\(v\) 的子树点权和。

对于每一个节点,暴力往上跳是很完蛋的,树剖或者倍增跳也很难,因为跳很多级父亲后 \(x\) 的增量根据 \(x\) 大小的不同也会不一样。

考虑一次跳跃,如果有 \(\min(size_v,x)=x\),那么 \(x\) 至少翻倍,称这个点为倍增点。注意到一个点 \(u\) 跳到根的过程中至多有 \(\mathcal{O}(\log \sum m)\) 个倍增点,而在跳到倍增点之前的路径,其对 \(x\) 的增量是很好预处理出然后快速求得的。

那么考虑一个倍增,\(f_{u,i}\) 代表从 \(u\) 跳到 \(u\)\(2^i\) 级父亲,要求 \(x\leq ?\) 才能保证不会遇到倍增点,这个是很好预处理的,那么处理一个点的答案的时候倍增找倍增点,然后跳到倍增点上,在倍增点上 \(x\) 的增量统计可以提前给这个节点的儿子点权和排序然后二分来快速求得。

时间复杂度为 \(\mathcal{O}(n\log n(\log n+\log m))\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#define pb emplace_back
#define mp std::make_pair
#define fi first
#define se second
typedef long long ll;
typedef std::pair<int, int> pii;
typedef std::pair<ll, int> pli;
typedef std::pair<ll, ll> pll;
typedef std::vector<int> vi;
typedef std::vector<ll> vll;
const ll mod = 998244353;
ll Add(ll x, ll y) { return (x+y>=mod) ? (x+y-mod) : (x+y); }
ll Mul(ll x, ll y) { return x * y % mod; }
ll Mod(ll x) { return x < 0 ? (x + mod) : (x >= mod ? (x-mod) : x); }
ll cadd(ll &x, ll y) { return x = (x+y>=mod) ? (x+y-mod) : (x+y); }
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
const int N = 300010;
int n, fa[N][20];
ll su[N][20], val[N], f[N][20];
vi eg[N];
vll s[N], va[N];
void dfs1(int x) {
	for(auto v : eg[x]) {
		dfs1(v);
		val[x] += val[v];
	}
}
void dfs2(int x) {
	for(int i = 1; i <= 19; ++i) fa[x][i] = fa[fa[x][i-1]][i-1];
	if(x) {
		if(va[fa[x][0]].size() == 1) f[x][0] = 0;
		else f[x][0] = va[fa[x][0]][(int)va[fa[x][0]].size()-1-(va[fa[x][0]][(int)va[fa[x][0]].size()-1] == val[x])];
		su[x][0] = s[fa[x][0]][(int)s[fa[x][0]].size()-1] - val[x];
		for(int i = 1; i <= 19; ++i)
			su[x][i] = su[x][i-1] + su[fa[x][i-1]][i-1],
			f[x][i] = Max(f[x][i-1], f[fa[x][i-1]][i-1] - su[x][i-1]);
	}
	for(auto v : eg[x]) dfs2(v);
}
signed main() {
//	freopen("data.in", "r", stdin);
//	freopen("1.out", "w", stdout);
	read(n);
	for(int i = 1; i <= n; ++i) {
		read(fa[i][0]); read(val[i]);
		eg[fa[i][0]].pb(i);
	}
	dfs1(0);
	for(int i = 1; i <= n; ++i) va[fa[i][0]].pb(val[i]), s[fa[i][0]].pb(0);
	for(int i = 0; i <= n; ++i) {
		std::sort(va[i].begin(), va[i].end());
		for(int j = 0; j < (int)va[i].size(); ++j)
			s[i][j] = s[i][j-1+(j==0)] + va[i][j];
	}
	dfs2(0);
	for(int i = 1; i <= n; ++i) {
		int x = i; ll t = val[x];
		while(x) {
			for(int j = 19; ~j; --j)
				if(x && t >= f[x][j]) {
					t += su[x][j];
					x = fa[x][j];
				}
			if(x) {
				int p = std::upper_bound(va[fa[x][0]].begin(), va[fa[x][0]].end(), t) - va[fa[x][0]].begin() - 1;
				ll lt = t;
				if(p<0) {
					t += lt * s[fa[x][0]].size();
					t -= Min(val[x], lt);
				}
				else {
					t += lt * (s[fa[x][0]].size()-p-1);
					t += s[fa[x][0]][p];
					t -= Min(val[x], lt);
				}
				x = fa[x][0];
			}
		}
		printf("%lld\n", t);
	}
	return 0;
}
posted @ 2021-10-06 20:45  do_while_true  阅读(62)  评论(0编辑  收藏  举报