CF 1843F2 - Omsk Metro (hard version)

CF 1843F2 - Omsk Metro (hard version)

题目描述

给定 n n n 个节点的有根树,根节点为 1 1 1,每个节点上有权值 − 1 -1 1 1 1 1

q q q 次询问,每次询问 u u u v v v 路径上的最大子段和。

数据范围

1 ≤ n ≤ 2 ⋅ 1 0 5 , 1 ≤ q ≤ 2 ⋅ 1 0 5 1 \leq n \leq 2 \cdot 10^5, 1 \leq q \leq 2 \cdot 10^5 1n2105,1q2105


题解报告

假设树的形状是一条链,即每次询问数组上一段连续区间内的最大子段和,

可以通过维护前缀和,计算询问区间内最大前缀和与最小前缀和,两者相减即为该询问区间的最大子段和。

对于树形结构,可以采用相同的思想,利用前缀和的差来计算答案。

考虑 u u u v v v 的祖先(或反过来)的情况,

答案即为路径上最大前缀和最小前缀的差;

假如 u u u v v v 之间不存在祖先关系,令 l c a lca lca 为两点的最近公共祖先,

则最大子段要么全部位于 l c a lca lca u u u 的路径中,要么全部位于 l c a lca lca v v v 的路径中,要么被 l c a lca lca 节点分成两部分;

对于前两种情况,和上述讨论是相同的;

对于第三种情况,答案即为 l c a lca lca u u u 之间的最大前缀加上 l c a lca lca v v v 之间的最大前缀减去 l c a lca lca 的前缀的两倍再加上 l c a lca lca 的权值。

归根到底都是求路径上的最大值和最小值,可以用树剖 + + + 线段树维护;

不过有更简单的方法,倍增。

n o d e [ i ] [ j ] node[i][j] node[i][j],表示第 i i i 个节点向上 2 j 2^j 2j 步之间所有节点(不包括终点)的前缀和的最大值和最小值,

更新方式很传统, n o d e [ i ] [ j ] = m a x / m i n ( n o d e [ i ] [ j − 1 ] , n o d e [ f a [ i ] [ j − 1 ] ] [ j − 1 ] ) node[i][j] = max/min (node[i][j - 1], node[fa[i][j - 1]][j - 1]) node[i][j]=max/min(node[i][j1],node[fa[i][j1]][j1])

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define int ll
using namespace std;
const int N = 2e5 + 5;
const int M = 20;

struct Node {
	int mx_suf, mx_seg;
	int mn_suf, mn_seg;
	
	void Merge (Node &a) {
		this -> mx_seg = max ({this -> mx_seg, a.mx_seg, this -> mx_suf - a.mn_suf});
		this -> mn_seg = min ({this -> mn_seg, a.mn_seg, this -> mn_suf - a.mx_suf});
		this -> mx_suf = max (this -> mx_suf, a.mx_suf);
		this -> mn_suf = min (this -> mn_suf, a.mn_suf);
	}
};

int Get_lca (int u, int v, vector <vector <int> > &fa, vector <int> &dep) {
	if (dep[u] < dep[v]) swap (u, v);
	for (int i = M - 1; i >= 0; --i) {
		if (dep[fa[u][i]] > dep[v]) u = fa[u][i];
	}
	if (dep[u] > dep[v]) u = fa[u][0];
	for (int i = M - 1; i >= 0; --i) {
		if (fa[u][i] != fa[v][i]) {
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	if (u != v) return fa[u][0];
	else return u;
}

Node Get_ans (int u, int lca, vector <vector <Node> > &node, 
vector <vector <int> > &fa, vector <int> &dep) {
	Node ans = {-1, -1, -1, -1};
	for (int i = M - 1; i >= 0; --i) {
		if (dep[fa[u][i]] >= dep[lca]) {
			if (ans.mx_seg == -1) ans = node[u][i];
			else ans.Merge (node[u][i]);
			u = fa[u][i];
		}
	}
	if (ans.mx_seg == -1) ans = node[u][0];
	else ans.Merge (node[u][0]);
	return ans;
}

int t, n;

void init () {}

void charming () {
	init ();
	cin >> n;
	vector <vector <int> > fa (n + 5, vector <int> (M));
	vector <vector <Node> > node (n + 5, vector <Node> (M));
	node[1][0] = (Node) {1, 1, 1, 0};
	vector <int> suf (n + 5), dep (n + 5), val (n + 5);
	suf[1] = dep[1] = val[1] = 1;
	for (int i = 1; i < M; ++i) node[1][i] = node[1][i - 1];
	char op;
	for (int i = 1, u, v, x, k, cnt = 1, mn, mx, lca; i <= n; ++i) {
		cin >> op;
		if (op == '+') {
			++cnt, cin >> v >> x;
			fa[cnt][0] = v;
			for (int i = 1; i < M; ++i) fa[cnt][i] = fa[fa[cnt][i - 1]][i - 1];
			suf[cnt] = suf[v] + x, dep[cnt] = dep[v] + 1, val[cnt] = x;
			node[cnt][0] = (Node) {suf[cnt], max (0ll, x), suf[cnt], min (0ll, x)};
			for (int i = 1; i < M; ++i) {
				node[cnt][i] = node[cnt][i - 1];
				node[cnt][i].Merge (node[fa[cnt][i - 1]][i - 1]);
			}
		} else {
			cin >> u >> v >> k;
			mn = mx = 0;
			lca = Get_lca (u, v, fa, dep);
			Node ans_u = Get_ans (u, lca, node, fa, dep);
			Node ans_v = Get_ans (v, lca, node, fa, dep);
			mn = min ({mn, ans_u.mn_seg, ans_v.mn_seg, ans_u.mn_suf + ans_v.mn_suf - suf[lca] * 2 + val[lca]});
			mx = max ({mx, ans_u.mx_seg, ans_v.mx_seg, ans_u.mx_suf + ans_v.mx_suf - suf[lca] * 2 + val[lca]});
			if (k >= mn && k <= mx) cout << "YES" << endl;
			else cout << "NO" << endl;
		}
	}
}

signed main () {
	cin >> t;
	while (t--) charming ();
	return 0;
}

后记

想了半天都没想出来怎么写的,看了别人的代码才发现这么简单…主要是刚开始方向都没对,到后面都想到了线段树合并那里了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Here is a list of time zones around the world, including non-hourly time zones: - UTC-12:00 - Baker Island, Howland Island - UTC-11:00 - Niue, Samoa, Midway Atoll - UTC-10:00 - Hawaii-Aleutian Standard Time (HST), French Polynesia, Cook Islands - UTC-09:30 - Marquesas Islands - UTC-09:00 - Alaska Standard Time (AKST), Gambier Islands - UTC-08:00 - Pacific Standard Time (PST), Clipperton Island - UTC-07:00 - Mountain Standard Time (MST), Chihuahua, Mazatlan - UTC-06:00 - Central Standard Time (CST), Belize, Costa Rica - UTC-05:00 - Eastern Standard Time (EST), Colombia, Cuba - UTC-04:30 - Venezuelan Standard Time - UTC-04:00 - Atlantic Standard Time (AST), Bolivia, Chile, Dominican Republic - UTC-03:30 - Newfoundland Standard Time - UTC-03:00 - Amazon Standard Time, Argentina, Brazil, Uruguay - UTC-02:00 - Fernando de Noronha, South Georgia and the South Sandwich Islands - UTC-01:00 - Azores Standard Time, Cape Verde - UTC+00:00 - Greenwich Mean Time (GMT), Iceland, Senegal - UTC+01:00 - Central European Time (CET), Algeria, Nigeria - UTC+02:00 - Eastern European Time (EET), Egypt, Israel - UTC+03:00 - Moscow Standard Time, Kenya, Saudi Arabia - UTC+03:30 - Iran Standard Time - UTC+04:00 - Gulf Standard Time, Armenia, Georgia - UTC+04:30 - Afghanistan Standard Time - UTC+05:00 - Pakistan Standard Time, Uzbekistan - UTC+05:30 - Indian Standard Time (IST) - UTC+05:45 - Nepal Standard Time - UTC+06:00 - Bangladesh Standard Time, Bhutan, Omsk Time - UTC+06:30 - Cocos Islands, Myanmar Standard Time - UTC+07:00 - Indochina Time, Western Indonesian Time - UTC+08:00 - China Standard Time (CST), Philippine Standard Time - UTC+08:45 - Southeastern Western Australia Standard Time - UTC+09:00 - Japan Standard Time (JST), Korea Standard Time - UTC+09:30 - Australian Central Standard Time - UTC+10:00 - Australian Eastern Standard Time (AEST), Vladivostok Time - UTC+10:30 - Lord Howe Island - UTC+11:00 - Solomon Islands, Vanuatu - UTC+11:30 - Norfolk Island - UTC+12:00 - New Zealand Standard Time (NZST), Fiji, Kamchatka Time - UTC+12:45 - Chatham Islands Standard Time - UTC+13:00 - Samoa Standard Time, Tonga - UTC+14:00 - Line Islands

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值