2021江苏省赛 Gym103495F. Jumping Monkey II 树分治

Problem – F – Codeforces

题意:给定一棵有点权的树,对每个点计算:从这个点开始走,能走出的所有简单路径,形成的点权序列的最长上升子序列(当然要包含自己)的长度的最大值。

$n\le 2 \times 10 ^ 5$


如果不分治的话,直接按照点权排序之后会发现要维护一个以入边为状态的 DP。这个东西的复杂度是和度数的平方有关的,并且没法优化。所以考虑点分治。

在处理某层时可能有多棵子树,为了保证复杂度需要再写一个小分治用来处理这些子树之间的转移(并且记得加上分治中心之后再去更新答案)。

另外有一个细节,如果只有一棵子树,那么需要特殊处理,用分治中心更新一下这棵子树中,否则按照代码的写法会漏掉。

线段树合并做法被卡了,题解说启发式合并可以过。待补。

UPD:傻了,分治内部是不用写小分治的,直接用前缀和后缀就可以凑出“除去某个子树”的信息了。更改的只有这一小段,其他的和下面的代码一样。

void dfs(int x, int seg, int pre, int suf) {
	dp_down[x] = max(query(1, N, seg, a[x] + 1), max(query(1, N, pre, a[x] + 1), query(1, N, suf, a[x] + 1))) + 1;
	seg = modify_add(1, N, seg, a[x], dp_down[x]);

	ans[x] = max(ans[x], dp_down[x]);

	for (int y : G[x])
		if (y != pr[x] && !vis[y])
			dfs(y, seg, pre, suf);
}

void divide_and_conquer(int x) {
	x = get_center(x);

	// printf("--- divide_and_conquer(%d) ---\n", x);

	vis[x] = true;

	vector<int> v;
	for (int y : G[x])
		if (!vis[y]) {
			v.push_back(y);
			pr[y] = x;
			prework(y);
		}
	
	int m = (int)v.size();

	ans[x] = max(ans[x], 1);

	if (m <= 1) {
		if (m) {
			ans[x] = max(ans[x], query(1, N, root[v[0]], a[x] + 1) + 1);

			int seg = modify_add(1, N, 0, a[x], 1);

			dfs(v[0], seg, 0, 0);
		}
	}
	else {
		vector<int> suf(m);
		
		int last = 0;
		for (int i = m - 1; ~i; i--)
			last = suf[i] = merge(1, N, last, root[v[i]]);

		last = 0;

		for (int i = 0; i < m; i++) {
			int su = i < m - 1 ? suf[i + 1] : 0, cnt = seg_cnt;

			int tmp = max(query(1, N, su, a[x] + 1), query(1, N, last, a[x] + 1)) + 1;
			dfs(v[i], modify_add(1, N, 0, a[x], tmp), last, su);

			seg_clear(cnt);

			ans[x] = max(ans[x], tmp);

			last = merge(1, N, last, root[v[i]]);
		}
	}

	seg_clear();

	for (int y : v) {
		for (int z : que[y]) {
			dp_up[z] = dp_down[z] = 0;
			pr[z] = 0;
			root[z] = 0;
		}

		que[y].clear();
	}

	for (int y : v)
		divide_and_conquer(y);
}

#include <bits/stdc++.h>

using namespace std;

constexpr int maxn = 200005, maxm = maxn * 19 * 2;

int val[maxm], lc[maxm], rc[maxm], seg_cnt;

int merge(int l, int r, int x, int y) {
	if (!x || !y)
		return x | y;
	
	int z = ++seg_cnt;

	val[z] = max(val[x], val[y]);

	if (l < r) {
		int mid = (l + r) / 2;

		lc[z] = merge(l, mid, lc[x], lc[y]);
		rc[z] = merge(mid + 1, r, rc[x], rc[y]);
	}

	return z;
}

int modify_add(int l, int r, int u, int p, int d) {
	int o = ++seg_cnt; // 持久化
	lc[o] = lc[u];
	rc[o] = rc[u];
	
	val[o] = max(val[u], d);

	if (l < r) {
		int mid = (l + r) / 2;
		
		if (p <= mid)
			lc[o] = modify_add(l, mid, lc[o], p, d);
		else
			rc[o] = modify_add(mid + 1, r, rc[o], p, d);
	}

	return o;
}

int query(int l, int r, int o, int s) {
	if (s > r)
		return 0;

	if (!o || s <= l)
		return val[o];
	
	int mid = (l + r) / 2, ans = query(mid + 1, r, rc[o], s);
	
	if (s <= mid)
		ans = max(ans, query(l, mid, lc[o], s));
	
	return ans;
}

void seg_clear() {
	for (int *a : {val, lc, rc})
		memset(a, 0, sizeof(int) * (seg_cnt + 1));

	seg_cnt = 0;
}

void seg_clear(int until) {
	for (int *a : {val, lc, rc})
		memset(a + until + 1, 0, sizeof(int) * (seg_cnt - until));

	seg_cnt = until;
}

vector<int> G[maxn];
int sz[maxn], pr[maxn], son[maxn];
bool vis[maxn];

int get_center(int rt) {
	static int q[maxn];

	int head = 0, tail = 0;
	q[tail++] = rt;

	while (head != tail) {
		int x = q[head++];
		sz[x] = 1;
		son[x] = 0;

		for (int y : G[x])
			if (y != pr[x] && !vis[y]) {
				pr[y] = x;
				q[tail++] = y;
			}
	}

	for (int i = tail - 1; i; i--) {
		int x = q[i];

		sz[pr[x]] += sz[x];
		if (sz[x] > sz[son[pr[x]]])
			son[pr[x]] = x;
	}

	int o = rt;
	while (son[o] && sz[son[o]] * 2 >= sz[rt])
		o = son[o];
	
	for (int i = 0; i < tail; i++) {
		int x = q[i];
		sz[x] = pr[x] = son[x] = 0;
	}

	return o;
}

int root[maxn];

int a[maxn];
int dp_up[maxn], dp_down[maxn]; // go up, or go down
int ans[maxn], N;

vector<int> que[maxn];

void prework(int rt) {
	vector<int> &q = que[rt];

	int head = 0;
	q.push_back(rt);

	while (head != (int)q.size()) {
		int x = q[head++];

		for (int y : G[x])
			if (y != pr[x] && !vis[y]) {
				pr[y] = x;
				q.push_back(y);
			}
	}

	for (int i = (int)q.size() - 1; ~i; i--) {
		int x = q[i];

		for (int y : G[x])
			if (y != pr[x] && !vis[y])
				root[x] = merge(1, N, root[x], root[y]);
		
		dp_up[x] = query(1, N, root[x], a[x] + 1) + 1;
		root[x] = modify_add(1, N, root[x], a[x], dp_up[x]);

		ans[x] = max(ans[x], dp_up[x]);
	}
}

void dfs(int x, int seg) {
	dp_down[x] = query(1, N, seg, a[x] + 1) + 1;
	seg = modify_add(1, N, seg, a[x], dp_down[x]);

	ans[x] = max(ans[x], dp_down[x]);

	for (int y : G[x])
		if (y != pr[x] && !vis[y])
			dfs(y, seg);
}

int solve(int x, vector<int> &v, int l, int r) {
	if (l == r)
		return root[v[l]];
	
	int mid = (l + r) / 2;
	
	int seg[2] = {solve(x, v, l, mid), solve(x, v, mid + 1, r)};
	int cnt = seg_cnt;

	int st[2];
	for (int i = 0; i < 2; i++) {
		int tmp = query(1, N, seg[i], a[x] + 1) + 1;
		ans[x] = max(ans[x], tmp);

		st[i] = modify_add(1, N, seg[i], a[x], tmp);
	}

	for (int i = l; i <= r; i++)
		dfs(v[i], st[i <= mid]);
	
	seg_clear(cnt);
	
	return merge(1, N, seg[0], seg[1]);
}

void divide_and_conquer(int x) {
	x = get_center(x);

	vis[x] = true;

	vector<int> v;
	for (int y : G[x])
		if (!vis[y]) {
			v.push_back(y);
			pr[y] = x;
			prework(y);
		}
	
	int m = (int)v.size();

	ans[x] = max(ans[x], 1);

	if (m <= 1) {
		if (m) {
			ans[x] = max(ans[x], query(1, N, root[v[0]], a[x] + 1) + 1);

			int seg = modify_add(1, N, 0, a[x], 1);

			dfs(v[0], seg);
		}
	}
	else {
		int seg = solve(x, v, 0, m - 1);

		ans[x] = max(ans[x], query(1, N, seg, a[x] + 1) + 1);
	}

	seg_clear();

	for (int y : v) {
		for (int z : que[y]) {
			dp_up[z] = dp_down[z] = 0;
			pr[z] = 0;
			root[z] = 0;
		}

		que[y].clear();
	}

	for (int y : v)
		divide_and_conquer(y);
}

int b[maxn];

int main() {

	int T;
	scanf("%d", &T);

	while (T--) {
		int n;
		scanf("%d", &n);
		N = n;

		for (int i = 1; i <= n; i++)
			scanf("%d", &a[i]);
		
		memcpy(b + 1, a + 1, sizeof(int) * n);

		sort(b + 1, b + n + 1);

		for (int i = 1; i <= n; i++)
			a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
		
		for (int i = 1; i < n; i++) {
			int x, y;
			scanf("%d%d", &x, &y);

			G[x].push_back(y);
			G[y].push_back(x);
		}

		divide_and_conquer(1);

		for (int i = 1; i <= n; i++)
			printf("%d\n", ans[i]);
		
		for (int i = 1; i <= n; i++) {
			ans[i] = 0;
			vis[i] = false;
			G[i].clear();
		}

		assert(seg_cnt == 0);
	}

	return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇