线段树的常见应用

线段树的常见应用

23南京M

题面

M. Trapping Rain Water

time limit per test: 5.0 s

memory limit per test: 1024 megabytes

input: standard input

output: standard output

There is a histogram represented by an integer sequence a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an of length n n n. For the i i i-th bar from left to right, its height is a i a_i ai and its width is 1 1 1.

We’ll perform q q q modifications to the histogram. The i i i-th modification can be represented by a pair of integers ( x i , v i ) (x_i, v_i) (xi,vi) indicating that we’ll increase the height of the x i x_i xi-th bar by v i v_i vi.

After each modification, answer the following query: Calculate how much water this histogram can trap if a heavy rain pours onto it and fills all the pits as much as possible.

More formally, given an integer sequence a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an of length n n n, the i i i-th modification will increase a x i a_{x_i} axi by v i v_i vi. After each modification, answer the following query: Let f i = max ⁡ ( a 1 , a 2 , ⋯   , a i ) f_i = \max(a_1, a_2, \cdots, a_i) fi=max(a1,a2,,ai) and g i = max ⁡ ( a i , a i + 1 , ⋯   , a n ) g_i = \max(a_i, a_{i + 1}, \cdots, a_n) gi=max(ai,ai+1,,an), calculate

∑ i = 1 n ( min ⁡ ( f i , g i ) − a i ) \sum\limits_{i = 1}^n \left( \min(f_i, g_i) - a_i \right) i=1n(min(fi,gi)ai)

Input

There are multiple test cases. The first line of the input contains an integer T T T indicating the number of test cases. For each test case:

The first line contains an integer n n n ( 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105) indicating the number of bars in the histogram.

The second line contains n n n integers a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an ( 1 ≤ a i ≤ 1 0 6 1 \le a_i \le 10^6 1ai106) where a i a_i ai indicates the initial height of the i i i-th bar.

The third line contains an integer q q q ( 1 ≤ q ≤ 1 0 5 1 \le q \le 10^5 1q105) indicating the number of modifications.

For the following q q q lines, the i i i-th line contains two integers x i x_i xi and v i v_i vi ( 1 ≤ x i ≤ n 1 \le x_i \le n 1xin, 1 ≤ v i ≤ 1 0 6 1 \le v_i \le 10^6 1vi106) indicating that the i i i-th modification increases the height of the x i x_i xi-th bar by v i v_i vi.

It is guaranteed that neither the sum of n n n nor the sum of q q q of all test cases will exceed 1 0 6 10^6 106.

Output

For each modification output one line containing one integer indicating how much rain water this histogram can trap.

题意

给出 n n n 个位置的高度, q q q 次询问为增加某个位置高度之后接雨水量。每次高度增加叠加

题解

首先注意到题目给出了一个接水量计算公式
∑ i = 1 n ( min ⁡ ( f i , g i ) − a i ) \sum\limits_{i = 1}^n \left( \min(f_i, g_i) - a_i \right) i=1n(min(fi,gi)ai)
这个公式有一个较为巧妙的转化, f i f_i fi g i g_i gi 分别是 i i i 位置的前后缀最大值,那么这两者中的较大值就是整个区间的最大值

所以就可以将 min ⁡ ( f i , g i ) = f i + g i − max ⁡ ( a ) \min(f_i, g_i) = f_i + g_i - \max(a) min(fi,gi)=fi+gimax(a)

由于这样就可以将求和拆开,分别维护 a a a 的前后缀最值和就可以了

考虑对于位置 x x x 增加高度 v v v

显然,只有 x x x 到其右侧第一个大于等于 a x + v a_x + v ax+v 的位置之间的 f i f_i fi 会增加 v v v,对于 g i g_i gi同理

查找 x x x 右侧第一个大于等于 a x + v a_x + v ax+v 的位置可以使用线段树维护最大值,再二分得到

max ⁡ ( a ) \max(a) max(a) 的修改和 a x + v a_x + v ax+v 比较即可

Code
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
using ll = long long;

const int N = 1e5 + 5; 

// 线段树维护前后缀区间和,以及前后缀区间最值,线段树上二分
// f为前缀最大值,g为后缀最大值
// min(f[i], g[i]) = f[i] + g[i] - max(a)
// 修改时进行线段树上二分
struct Node {
	ll sum, mx, tag;
};

class Segment {
public:
	int n;
	ll a[N];
	Node node[N << 2];
	void resize (int _n, ll *p) {
		n = _n;
		for (int i = 1; i <= n; i ++) a[i] = p[i];
	}
	void push_up (int p) {
		node[p].sum = node[p << 1].sum + node[p << 1 | 1].sum;
		node[p].mx = max(node[p << 1].mx, node[p << 1 | 1].mx);
	}
	void build (int s, int t, int p) {
		node[p].tag = 0;
		if (s == t) {
			node[p].mx = a[s];
			node[p].sum = a[s];
			node[p].tag = 0;
			return;
		}
		int mid = (s + t) >> 1;
		build(s, mid, p << 1);
		build(mid + 1, t, p << 1 | 1);
		push_up(p);
	}
	void push_down (int s, int t, int p) {
		if (node[p].tag) {
			int mid = (s + t) >> 1;
			int ls = p << 1, rs = p << 1 | 1;
			node[ls].tag = node[p].tag;
			node[rs].tag = node[p].tag;
			node[ls].mx = node[p].tag;
			node[rs].mx = node[p].tag;
			node[ls].sum = (mid - s + 1) * node[p].tag;
			node[rs].sum = (t - mid) * node[p].tag;
			node[p].tag = 0;
		}
	}
	void updete (int s, int t, int l, int r, int p,ll  v) {
		if (l <= s && t <= r) {
			node[p].sum = (t - s + 1) * v;
			node[p].mx = v;
			node[p].tag = v;
			return ;
		}
		int mid = (s + t) >> 1;
		push_down(s, t, p);
		if (l <= mid) updete(s, mid, l, r, p << 1, v);
		if (mid < r) updete(mid + 1, t, l, r, p << 1 | 1, v);
		push_up(p);
		return;
	}
	ll getsum (int s, int t, int l, int r, int p) {
		if (l <= s && t <= r) return node[p].sum;
		int mid = (s + t) >> 1;
		push_down(s, t, p);
		ll res = 0;
		if (l <= mid) res = getsum(s, mid, l, r, p << 1);
		if (mid < r) res += getsum(mid + 1, t, l, r, p << 1 | 1);
		push_up(p);
		return res;
	}
	int query_f (int s, int t, int l, int r, int p, ll k) { // 前缀
		int mid = (s + t) >> 1;
		if (l <= s && t <= r) {
			if (node[p].mx <= k) return 0;
			if (s == t) return s;
			push_down(s, t, p);
			if (node[p << 1].mx <= k) return query_f(mid + 1, t, l, r, p << 1 | 1, k);
			return query_f(s, mid, l, r, p << 1, k);
		}
		push_down(s, t, p);
		if (r <= mid) return query_f(s, mid, l, r, p << 1, k);
		if (l > mid) return query_f(mid + 1, t, l, r, p << 1 | 1, k);
		int res = query_f(s, mid, l, r, p << 1, k);
		if (res == 0) return query_f(mid + 1, t, l, r, p << 1 | 1, k);
		return res;
	}
	int query_g (int s, int t, int l, int r, int p, ll k) { // 后缀
		int mid = (s + t) >> 1;
		if (l <= s && t <= r) {
			if (node[p].mx <= k) return 0;
			if (s == t) return s;
			push_down(s, t, p);
			if (node[p << 1 | 1].mx <= k) return query_g(s, mid, l, r, p << 1, k);
			else return query_g(mid + 1, t, l, r, p << 1 | 1, k);
		}
		push_down(s, t, p);
		if (r <= mid) return query_g(s, mid, l, r, p << 1, k);
		if (l > mid) return query_g(mid + 1, t, l, r, p << 1 | 1, k);
		int res = query_g(mid + 1, t, l, r, p << 1 | 1, k);
		if (res == 0) return query_g(s, mid, l, r, p << 1, k);
		return res;
	}
};

int n, m;
ll a[N], f[N], g[N], sum, maxa;

Segment seg_f, seg_g;

void init () {
	sum = 0;
	maxa = 0;
}

void solve() {
	cin >> n;
	init();
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		f[i] = max(f[i - 1], a[i]);
		sum += a[i];
	}
	maxa = f[n];
	g[n + 1] = 0;
	for (int i = n; i > 0; i --) {
		g[i] = max(g[i + 1], a[i]);
	}
	seg_f.resize(n, f);
	seg_f.build(1, n, 1);
	seg_g.resize(n, g);
	seg_g.build(1, n, 1);
	cin >> m;
	while (m --) {
		int x;
		ll v;
		cin >> x >> v;
		a[x] += v;
		int pos_f = seg_f.query_f(1, n, x, n, 1, a[x]);
		if (pos_f > x) seg_f.updete(1, n, x, pos_f - 1, 1, a[x]);
		else if (pos_f == 0) seg_f.updete(1, n, x, n, 1, a[x]);
		int pos_g = seg_g.query_g(1, n, 1, x, 1, a[x]);
		if (pos_g < x && pos_g != 0) seg_g.updete(1, n, pos_g + 1, x, 1, a[x]);
		else if (pos_g == 0 && seg_g.getsum(1, n, 1, 1, 1) <= a[x]) seg_g.updete(1, n, 1, x, 1, a[x]);
	
		sum += v;
		maxa = max(maxa, a[x]);
		ll sum_f = seg_f.getsum(1, n, 1, n, 1);
		ll sum_g = seg_g.getsum(1, n, 1, n, 1);
		
		ll res = sum_f + sum_g - maxa * n - sum;
		cout << res << endl;
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

20澳门J

题面

J. Jewel Grab

time limit per test: 3 seconds

memory limit per test: 512 megabytes

input: standard input

output: standard output

The museum in Byteland has n n n jewels on display. These jewels are arranged in a row, labeled by 1 , 2 , … , n 1,2,\dots,n 1,2,,n from left to right. Every precious stone is of one of n n n distinct colors. The color of the jewel in the i i i-th leftmost place is c i c_i ci while the value of it is v i v_i vi.

Grammy, the master thief, aims to steal some jewels from this museum. The museum is secured by some quite expensive alarms that unfortunately can not be hacked.

Grammy invented a device: a robotic hand that can grab some jewels without triggering any of the alarms. The hand will start at the s s s-th leftmost place, moving right one by one, taking all the jewels lying below the hand in the whole grab procedure (including the s s s-th jewel), and finish at any place Grammy likes. Grammy could easily take all the jewels using the device, but she knows that the more she takes, the harder it will be to get rid of them. She decided that the safest way is to take a set of jewels such that no two taken jewels share the same color. But soon she realized that in such a way she would miss many jewels, so she would like to control the device to skip no more than k k k jewels in total, in such a way the skipped jewels will not be taken.

Grammy lost most of her money betting on programming contests. So she may do the grab many times. There will be m m m events of two types, detailed below:

  • “1 x c v” ( 1 ≤ x ≤ n 1\leq x\leq n 1xn, 1 ≤ c ≤ n 1\leq c\leq n 1cn, 1 ≤ v ≤ 1 0 9 1\leq v\leq 10^9 1v109): The museum replaces the jewel at the x x x-th leftmost place with a jewel whose color and value are c c c and v v v respectively.
  • “2 s k” ( 1 ≤ s ≤ n 1\leq s\leq n 1sn, 0 ≤ k ≤ 10 0\leq k\leq 10 0k10): Grammy plans to do a new grab. She will control the hand to start at the s s s-th leftmost place, and will skip no more than k k k jewels. Please write a program to compute the maximum total value of jewels she can take in this grab. Note that the museum will always put backup jewels to all the stolen places before the next event.

Input

The input contains only a single case.

The first line of the input contains two integers n n n and m m m ( 1 ≤ n , m ≤ 200   000 1 \leq n,m \leq 200\,000 1n,m200000), denoting the number of jewels and the number of events.

In the next n n n lines, the i i i-th line ( 1 ≤ i ≤ n ) (1 \le i \le n) (1in) contains two integers c i c_i ci and v i v_i vi ( 1 ≤ c i ≤ n 1\le c_i\leq n 1cin, 1 ≤ v i ≤ 1 0 9 1\leq v_i\leq 10^9 1vi109), describing the i i i-th jewel.

Each of the next m m m lines describes an event in formats described in the statement above.

Output

For each event of the second type, print a single line containing an integer, the maximum total value of jewels that can be achieved.

题意

n n n 件珠宝颜色为 c i c_i ci 价值为 v i v_i vi

m m m 次询问分为两种类型,第一种将第 x x x 件珠宝变为颜色 c c c 价值 v v v

第二种为查询从 s s s 位置向右的一段区间,跳过最多 k k k 件珠宝,剩余的珠宝颜色各不相同的价值之和最大为多少

题解

对于每种颜色只出现一次这个条件,不难想到一种常用技巧,使用 p r e i pre_i prei 记录位置 i i i 的珠宝颜色上一次出现的位置。对于 p r e pre pre 建树,可以使用线段树上二分得到从 s s s 向右的一段颜色不重复区间

注意到 k k k 的范围很小,所以对于重复的颜色直接暴力替换,每次询问进行 O ( k ) O(k) O(k) 次查找即可

对于修改操作,需要修改点 x x x 以及和 c x c_x cx(原颜色) 相同颜色的前后两点,以及和 c c c 颜色相同的前后两点,为了方便,由于 c c c 范围不大,可以对每个颜色的出现位置用 s e t set set 维护

Code
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define endl '\n'

const int maxn = 2e5 + 10;

int n, m;
int pre[maxn], c[maxn];
set <int> pos[maxn];
ll v[maxn];

class BIT{
private:
	ll _sum[maxn], _n;
public:
	void init (int sz) {
		_n = sz;
		for (int i = 1; i <= _n; i ++) _sum[i] = 0;
	}
	int lowbit (int x) {
		return x & (-x);
	}
	void add (int x, ll k) {
		while (x <= _n) {
			_sum[x] += k;
			x += lowbit(x);
		}
	}
	ll getsum (int x) {
		ll res = 0;
		while (x > 0) {
			res += _sum[x];
			x -= lowbit(x);
		}
		return res;
	}
};

int mx[maxn << 2];

void pushup (int p) {
	mx[p] = max (mx[p << 1], mx[p << 1 | 1]);
}
void build (int p, int s, int t) {
	if (s == t) {
		mx[p] = pre[s];
		return;
	}
	int mid = (s + t) >> 1;
	build(p << 1, s, mid);
	build(p << 1 | 1, mid + 1, t);
	pushup(p);
}
void update (int p, int s, int t, int k, int val) {
	if (s == t) {
		mx[p] = val;
		return;
	}
	int mid = (s + t) >> 1;
	if (k <= mid) update(p << 1, s, mid ,k, val);
	else update(p << 1 | 1, mid + 1, t, k, val);
	pushup(p);
}
int query (int p, int s, int t, int l, int r, int k) {
	if (s > r || t < l || mx[p] < k) return 0;
	if (s == t) return mx[p] >= k ? s : 0;
	int mid = (s + t) >> 1;
	int res = query(p << 1, s, mid , l, r, k);
	if (res) return res;
	return query(p << 1 | 1, mid + 1, t, l, r, k);
}

void change (int x, int col, ll val, BIT &bit) {
	bit.add (x, val - v[x]);
	v[x] = val;
	auto l = pos[c[x]].lower_bound(x);
	auto r = pos[c[x]].upper_bound(x);
	l --;
	if (*r != n + 1) {
		pre[*r] = *l;
		update(1, 1, n, *r, pre[*r]);
	}
	pos[c[x]].erase(x);
	c[x] = col;
	pos[c[x]].insert(x);
	l = pos[c[x]].lower_bound(x);
	r = pos[c[x]].upper_bound(x);
	l --;
	if (*r != n + 1) {
		pre[*r] = x;
		update(1, 1, n, *r, pre[*r]);
	}
	pre[x] = *l;
	update(1, 1, n, x, pre[x]);
}

int tmp[maxn];
ll get (int x, int k, BIT &bit) {
	ll res = 0;
	int p = x;
	vector<int> vt;
	while (p <= n) {
		int nxt = query(1, 1, n, p, n, x);
		if (!nxt) {
			res += bit.getsum(n) - bit.getsum(p - 1);
			break;
		}
		res += bit.getsum(nxt - 1) - bit.getsum(p - 1);
		if (!k) break;
		if (!tmp[c[nxt]]) {
			tmp[c[nxt]] = v[pre[nxt]];
			vt.push_back(c[nxt]);
		}
		if (tmp[c[nxt]] < v[nxt]){
			res += v[nxt] - tmp[c[nxt]];
			tmp[c[nxt]] = v[nxt];
		}
		p = nxt + 1;
		k --;
	}
	for (auto x: vt) tmp[x] = 0;
	return res;
}

void solve () {
	cin >> n >> m;
	BIT bit;
	bit.init (n);
	for (int i = 1; i <= n; i ++) {
		cin >> c[i] >> v[i];
		pos[c[i]].insert(i);
		pos[i].insert(0);
		pos[i].insert(n + 1);
		bit.add (i, v[i]);
	}
	for (int i = 1; i <= n; i ++) {
		int last = 0;
		for (auto x: pos[i]) {
			if (x < 1 || x > n) continue;
			pre[x] = last;
			last = x;
		}
	}
	build(1, 1, n);
	while (m --){
		int opt;
		cin >> opt;
		if (opt == 1) {
			int x, col;
			ll val;
			cin >> x >> col >> val;
			change(x, col, val, bit);
		} else {
			int s, k;
			cin >> s >> k;
			cout << get(s, k, bit) << endl;
		}
	}
} 

signed main () {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int _ = 1;
//	cin >> _;
	while (_ --) {
		solve();
	}
	return 0;
}

CFedu46 F

题面

F. One Occurrence

time limit per test: 3 seconds

memory limit per test: 768 megabytes

input: standard input

output: standard output

You are given an array a a a consisting of n n n integers, and q q q queries to it. i i i-th query is denoted by two integers l i l_i li and r i r_i ri. For each query, you have to find any integer that occurs exactly once in the subarray of a a a from index l i l_i li to index r i r_i ri (a subarray is a contiguous subsegment of an array). For example, if a = [ 1 , 1 , 2 , 3 , 2 , 4 ] a = [1, 1, 2, 3, 2, 4] a=[1,1,2,3,2,4], then for query ( l i = 2 , r i = 6 ) (l_i = 2, r_i = 6) (li=2,ri=6) the subarray we are interested in is [ 1 , 2 , 3 , 2 , 4 ] [1, 2, 3, 2, 4] [1,2,3,2,4], and possible answers are 1 1 1, 3 3 3 and 4 4 4; for query ( l i = 1 , r i = 2 ) (l_i = 1, r_i = 2) (li=1,ri=2) the subarray we are interested in is [ 1 , 1 ] [1, 1] [1,1], and there is no such element that occurs exactly once.

Can you answer all of the queries?

Input

The first line contains one integer n n n ( 1 ≤ n ≤ 5 ⋅ 1 0 5 1 \le n \le 5 \cdot 10^5 1n5105).

The second line contains n n n integers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an ( 1 ≤ a i ≤ 5 ⋅ 1 0 5 1 \le a_i \le 5 \cdot 10^5 1ai5105).

The third line contains one integer q q q ( 1 ≤ q ≤ 5 ⋅ 1 0 5 1 \le q \le 5 \cdot 10^5 1q5105).

Then q q q lines follow, i i i-th line containing two integers l i l_i li and r i r_i ri representing i i i-th query ( 1 ≤ l i ≤ r i ≤ n 1 \le l_i \le r_i \le n 1lirin).

Output

Answer the queries as follows:

If there is no integer such that it occurs in the subarray from index l i l_i li to index r i r_i ri exactly once, print 0 0 0. Otherwise print any such integer.

题意

给出长为 n n n 的数组 a a a q q q 次询问,每次询问需要输出区间 ( l , r ) (l, r) (l,r) 内只出现过一次的任意一个数

题解

对于这种只出现一次的要求,还是使用常用技巧,使用 l a s t i last_i lasti 维护 a i a_i ai 上一次出现的位置。

为了统计区间内出现次数唯一的个数,建立可持久化权值线段树,对于每个数出现的位置初始化为极大值。

对于查询 ( l , r ) (l, r) (l,r) 内只出现一次的数,需要在 r r r 版本中查找最近位置小于 l l l 的数。所以在向上合并时,可以直接选择出现位置更靠前的数,查询和维护就是区间最小值。

Code
#include<bits/stdc++.h>
using namespace std;

const int N = 5e5 + 10;
#define endl '\n'
const int INF = 1e9 + 10;

struct Node {
	int lson, rson;
	int p;
	int val;
} node[N << 6];
int idx = 0;

int n, m;
int a[N], last[N];
#define ls(p) node[p].lson
#define rs(p) node[p].rson
//使用可持久化权值线段树存每个数最近出现的位置
//查询时需要(l,r)的最近位置最小值小于l

int build (int s, int t) {
	int p = ++ idx;
	node[p].val = INF;
	if (s == t) {
		node[p].p = s;
		return p;
	}
	int mid = (s + t) >> 1;
	node[p].lson = build(s, mid);
	node[p].rson = build(mid + 1, t);
	if (node[ls(p)].val < node[rs(p)].val) {
		node[p].val = node[ls(p)].val;
		node[p].p = node[ls(p)].p;
	} else {
		node[p].val = node[rs(p)].val;
		node[p].p = node[rs(p)].p;
	}
	return p;
}

int insert (int p, int s, int t, int x, int val) {
	int q = ++ idx;
	node[q] = node[p];
	if (s == t) {
		node[q].val = val;
		return q;
	}
	int mid = (s + t) >> 1;
	if (x <= mid) node[q].lson = insert(node[q].lson, s, mid, x, val);
	else node[q].rson = insert(node[q].rson, mid + 1, t, x, val);
	if (node[ls(q)].val < node[rs(q)].val) {
		node[q].val = node[ls(q)].val;
		node[q].p = node[ls(q)].p;
	} else {
		node[q].val = node[rs(q)].val;
		node[q].p = node[rs(q)].p;
	}
	return q;
}

Node query (int p, int s, int t ,int l, int r) {
	if (l <= s && t <= r) return node[p];
	int mid = (s + t) >> 1;
	if (mid < l) return query(node[p].rson, mid + 1, t, l, r);
	if (r <= mid) return query(node[p].lson, s, mid, l, r);
	Node res1 = query(node[p].lson, s, mid, l, r);
	Node res2 = query(node[p].rson, mid + 1, t, l, r);
	if (res1.val < res2.val) return res1;
	return res2;
}

int rt[N];

void solve(){
	cin >> n;
	rt[0] = build(1, n);
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		if (last[a[i]] == 0) {
			rt[i] = insert(rt[i - 1], 1, n, i, 0);
		} else {
			rt[i] = insert(rt[i - 1], 1, n, last[a[i]], INF);
			rt[i] = insert(rt[i], 1, n, i, last[a[i]]);
		}
		last[a[i]] = i;
	}
	cin >> m;
	while (m --) {
		int l, r;
		cin >> l >> r;
		Node now = query(rt[r], 1, n, l, r);
		if (now.val >= l) cout << 0 << endl;
		else cout << a[now.p] << endl;
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
//	cin >> _;
	while (_--) solve(); 
	return 0;
}

CFedu22 E

题面

E. Army Creation

time limit per test2 seconds

memory limit per test256 megabytes

input: standard input

output: standard output

As you might remember from our previous rounds, Vova really likes computer games. Now he is playing a strategy game known as Rage of Empires.

In the game Vova can hire n n n different warriors; i i ith warrior has the type a i a_i ai. Vova wants to create a balanced army hiring some subset of warriors. An army is called balanced if for each type of warrior present in the game there are not more than k warriors of this type in the army. Of course, Vova wants his army to be as large as possible.

To make things more complicated, Vova has to consider q different plans of creating his army. i i ith plan allows him to hire only warriors whose numbers are not less than li and not greater than r i r_i ri.

Help Vova to determine the largest size of a balanced army for each plan.

Be aware that the plans are given in a modified way. See input section for details.

Input
The first line contains two integers n n n and k ( 1   ≤   n ,   k   ≤   100000 ) k (1 \leq n, k \leq 100000) k(1 n,k 100000).

The second line contains n n n integers a 1 , a 2 , . . . a n ( 1   ≤   a i   ≤   100000 ) a_1, a_2, ... a_n (1 \leq a_i \leq 100000) a1,a2,...an(1 ai 100000).

The third line contains one integer q ( 1   ≤   q   ≤   100000 ) q (1 \leq q \leq 100000) q(1 q 100000).

Then q q q lines follow. ith line contains two numbers x i x_i xi and y i y_i yi which represent i i ith plan ( 1   ≤   x i ,   y i   ≤   n ) (1 \leq x_i, y_i \leq n) (1 xi,yin).

You have to keep track of the answer to the last plan (let’s call it last). In the beginning l a s t   =   0 last = 0 last= 0. Then to restore values of li and ri for the ith plan, you have to do the following:

l i   =   ( ( x i   +   l a s t ) m o d n )   +   1 l_i = ((x_i + last) mod n) + 1 li=((xi+last)modn)+ 1;
r i   =   ( ( y i   +   l a s t ) m o d n )   +   1 r_i = ((y_i + last) mod n) + 1 ri=((yi+last)modn)+ 1;
If l i   >   r i l_i > ri li>ri , swap l i l_i li and r i r_i ri.

Output
Print q q q numbers. i i ith number must be equal to the maximum size of a balanced army when considering i i ith plan.

题意

给出长为 n n n 的数组 a a a q q q 次询问,查询区间 ( l , r ) (l, r) (l,r) 之间相同数选择至多 k k k 个的最大数量,强制在线

题解

由于强制在线,所以不能使用莫队

对于至多 k k k 个相同数字,可以采取和前两题类似的技巧。使用 b i b_i bi 表示 a i a_i ai 前第 k k k 个相同数字出现的位置

建立可持久化权值线段树维护 b b b 数组值域区间和,对于查询 ( l , r ) (l, r) (l,r) r r r 版本上求 ( 0 , l − 1 ) (0, l - 1) (0,l1) 的和即可

Code
#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
#define endl '\n'
const int INF = 1e9 + 10;

struct Node {
	int lson, rson;
	int sum;
} node[N << 5];
int idx = 0;

int n, m;

// 主席树统计(l,r)之间有多少b小于l
// b记录每个位置的数左边第k个相同数的位置,小于k个为0

#define ls(p) node[p].lson
#define rs(p) node[p].rson

int build (int s, int t) {
	int p = ++ idx;
	if (s == t) {
		node[p].sum = 0;
		return p;
	}
	int mid = (s + t) >> 1;
	node[p].lson = build(s, mid);
	node[p].rson = build(mid + 1, t);
	node[p].sum = node[ls(p)].sum + node[rs(p)].sum;
	return p;
}

int insert (int p, int s, int t, int k) {
	int q = ++ idx;
	node[q] = node[p];
	if (s == t) {
		node[q].sum ++;
		return q;
	}
	int mid = (s + t) >> 1;
	if (k <= mid) ls(q) = insert(ls(q), s, mid, k);
	else rs(q) = insert(rs(q), mid + 1, t, k);
	node[q].sum = node[ls(q)].sum + node[rs(q)].sum;
	return q;
}

int query (int u, int v ,int s, int t, int k) {
	if (t <= k) return node[v].sum - node[u].sum;
	if (k < s) return 0;
	int mid = (s + t) >> 1;
	if (k <= mid) return query(ls(u), ls(v), s, mid, k);
	else return query(ls(u), ls(v), s, mid, k) + query(rs(u), rs(v), mid + 1, t, k);
}

int a[N], b[N];
vector<int> pos[N];
int rt[N];

void solve(){
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		pos[a[i]].push_back(i);
		if (pos[a[i]].size() <= m) b[i] = 0;
		else {
			b[i] = pos[a[i]][pos[a[i]].size() - m - 1];
		}
	}
	rt[0] = build(0, 100000);
	for (int i = 1; i <= n; i ++) {
		rt[i] = insert(rt[i - 1], 0, 100000, b[i]);
	}
	
	cin >> m;
	int last = 0;
	while (m --) {
		int L, R;
		cin >> L >> R;
		int l = (L + last) % n + 1;
		int r = (R + last) % n + 1;
		if (l > r) swap(l, r);
		last = query(rt[l - 1], rt[r], 0, 100000, l - 1);
		cout << last << endl;
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
//	cin >> _;
	while (_--) solve(); 
	return 0;
}

CF Round 837 F

题面

F. Hossam and Range Minimum Query

time limit per test: 1.5 seconds

memory limit per test: 256 megabytes

input: standard input

output: standard output

Hossam gives you a sequence of integers a 1 ,   a 2 ,   … ,   a n a_1, \, a_2, \, \dots, \, a_n a1,a2,,an of length n n n. Moreover, he will give you q q q queries of type ( l ,   r ) (l, \, r) (l,r). For each query, consider the elements a l ,   a l + 1 ,   … ,   a r a_l, \, a_{l + 1}, \, \dots, \, a_r al,al+1,,ar. Hossam wants to know the smallest number in this sequence, such that it occurs in this sequence an odd number of times.

You need to compute the answer for each query before process the next query.

Input

The first line of the input contains one integer n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105), the length of the sequence.

The second line contains n n n integers a 1 ,   a 2 ,   … ,   a n a_1, \, a_2, \, \dots, \, a_n a1,a2,,an ( 1 ≤ a i ≤ 1 0 9 1 \le a_i \le 10^9 1ai109).

The third line contains one integer q q q ( 1 ≤ q ≤ 2 ⋅ 1 0 5 1 \le q \le 2 \cdot 10^5 1q2105), the number of queries.

Each of the next q q q lines contains two integers a a a and b b b ( 0 ≤ a ,   b ≤ 2 ⋅ 1 0 9 0 \le a, \, b \le 2 \cdot 10^9 0a,b2109), the numbers used to encode the queries.

Let a n s i \mathrm{ans}_i ansi be the answer on the i i i-th query, and a n s 0 \mathrm{ans}_0 ans0 be zero. Then

l i = a i ⊕ a n s i − 1 , l_i = a_i \oplus \mathrm{ans}_{i - 1}, li=aiansi1,
r i = b i ⊕ a n s i − 1 , r_i = b_i \oplus \mathrm{ans}_{i - 1}, ri=biansi1,
where l i ,   r i l_i, \, r_i li,ri are parameters of the i i i-th query and ⊕ \oplus means the bitwise exclusive or operation. It is guaranteed that 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn.

Output

For each query, print the smallest number that occurs an odd number of times on the given segment of the sequence.

If there is no such number, print 0 0 0.

题意

给定长为 n n n 的数组 a a a q q q 次查询 ( l , r ) (l, r) (l,r) 之间出现奇数次的最小数字,强制在线

题解

由于强制在线,无法使用莫队

本题的重点在于如何判断某个数字出现次数的奇偶。

观察到题目给出的 a a a 值域范围较大,考虑使用异或和来判断奇偶。在数据随机的情况下,产生冲突的概率极小。

所以可以对 a a a 数组进行随机哈希映射到相同值域,然后建立可持久化权值线段树维护区间异或和。对于查询 ( l , r ) (l, r) (l,r) 在线段树上二分 l , r l, r l,r 版本异或值不为 0 0 0 的最小值即可

Code
#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;
#define endl '\n'
const int INF = 1e9 + 10;

int n, m;
int a[N];

// 主席树维护区间异或和
// 为每个值随机生成一个大于0的值作为标识
// 区间异或和为0,可认为都是偶数个,因为冲突的概率很小

struct Node {
	int lson, rson;
	int val;
} node[N << 6];
#define ls(x) node[x].lson
#define rs(x) node[x].rson
int idx;
int build (int s, int t) {
	int p = ++idx;
	if (s == t) {
		node[p].val = 0;
		return p;
	}
	int mid = (s + t) >> 1;
	ls(p) = build(s, mid);
	rs(p) = build(mid + 1, t);
	return p;
}
int update (int p, int s, int t, int x, int val) {
	int q = ++idx;
	node[q] = node[p];
	if (s == t) {
		node[q].val ^= val;
		return q;
	}
	int mid = (s + t) >> 1;
	if (x <= mid) ls(q) = update(ls(q), s, mid, x, val);
	else rs(q) = update(rs(q), mid + 1, t, x, val);
	node[q].val = node[ls(q)].val ^ node[rs(q)].val;
	return q;
}

int query (int u, int v, int s, int t) {
	if (s == t) return s;
	int mid = (s + t) >> 1;
	int res = node[ls(v)].val ^ node[ls(u)].val;
	if (res != 0) return query(ls(u), ls(v), s, mid);
	else return query(rs(u), rs(v), mid + 1, t);
}

int rt[N];
map<int, int> mp;

void solve(){
	cin >> n;
	for (int i = 1; i <= n; i ++) cin >> a[i];
	srand(time(0));
	for (int i = 1; i <= n; i ++) {
		if (!mp[a[i]]) {
			mp[a[i]] = rand() * rand() % INF + 1;
		}
		rt[i] = update(rt[i - 1], 1, INF, a[i], mp[a[i]]);
	}
	cin >> m;
	int ans = 0;
	while (m --) {
		int L, R;
		cin >> L >> R;
		int l = L ^ ans;
		int r = R ^ ans;
		if (l > r) swap(l, r);
		ans = query(rt[l - 1], rt[r], 1, INF);
		if (ans == INF) ans = 0;
		cout << ans << endl;
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
//	cin >> _;
	while (_--) solve(); 
	return 0;
}

CF Round 921 E

题面

E. Space Harbour

time limit per test: 2 seconds

memory limit per test: 256 megabytes

input: standard input

output: standard output

There are n n n points numbered 1 1 1 to n n n on a straight line. Initially, there are m m m harbours. The i i i-th harbour is at point X i X_i Xi and has a value V i V_i Vi. It is guaranteed that there are harbours at the points 1 1 1 and n n n. There is exactly one ship on each of the n n n points. The cost of moving a ship from its current location to the next harbour is the product of the value of the nearest harbour to its left and the distance from the nearest harbour to its right. Specifically, if a ship is already at a harbour, the cost of moving it to the next harbour is 0 0 0.

Additionally, there are q q q queries, each of which is either of the following 2 2 2 types:

  • 1 1 1 x x x v v v — Add a harbour at point x x x with value v v v. It is guaranteed that before adding the harbour, there is no harbour at point x x x.
  • 2 2 2 l l l r r r — Print the sum of the cost of moving all ships at points from l l l to r r r to their next harbours. Note that you just need to calculate the cost of moving the ships but not actually move them.

Input

The first line contains three integers n n n, m m m, and q q q ( 2 ≤ m ≤ n ≤ 3 ⋅ 1 0 5 2 \le m \le n \le 3 \cdot 10^5 2mn3105, 1 ≤ q ≤ 3 ⋅ 1 0 5 1 \le q \le 3 \cdot 10^5 1q3105) — the number of points, harbours, and queries, respectively.

The second line contains m m m distinct integers X 1 , X 2 , … , X m ( 1 ≤ X i ≤ n ) X_1, X_2, \ldots, X_m(1 \le X_i \le n) X1,X2,,Xm(1Xin) — the position at which the i i i-th harbour is located.

The third line contains m m m integers V 1 , V 2 , … , V m ( 1 ≤ V i ≤ 1 0 7 ) V_1, V_2, \ldots, V_m(1 \le V_i \le 10^7) V1,V2,,Vm(1Vi107) — the value of the i i i-th harbour.

Each of the next q q q lines contains three integers. The first integer is t t t ( 1 ≤ t ≤ 2 1\le t \le 2 1t2) — type of query. If t = 1 t=1 t=1, then the next two integers are x x x and v v v ( 2 ≤ x ≤ n − 1 2 \le x \le n - 1 2xn1, 1 ≤ v ≤ 1 0 7 1 \le v \le 10^7 1v107) — first-type query. If t = 2 t=2 t=2, then the next two integers are l l l and r r r ( 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn) — second-type query.

It is guaranteed that there is at least one second-type query.

Output

For every second-type query, print one integer in a new line — answer to this query.

题意

在一条直线上有 n n n 个编号为 1 1 1 n n n 的点。最初有 m m m 个港口。 i i i 个海港位于 X i X_i Xi 点,其值为 V i V_i Vi保证在 1 1 1 点和 n n n 点都有港口。将一艘船从当前位置移动到下一个港口的成本是其左侧最近港口的价值与右侧最近港口距离的乘积。具体来说,如果一艘船已经在一个港口,那么将它移动到下一个港口的成本是 0 0 0

此外,还有 q q q 个查询,每个查询都是以下 2 2 2 种类型中的一种:

  • 1 1 1 x x x v v v - 在 x x x 点添加一个海港,其值为 v v v 。保证在添加海港之前, x x x 点没有海港。
  • 2 2 2 l l l r r r - 打印将 l l l r r r 点的所有船只移至下一个港口的费用总和。注意,您只需要计算移动船只的费用,而不需要实际移动船只
题解

查询操作是 ( l , r ) (l, r) (l,r) 范围内的船只成本和,所以需要维护区间和

考虑添加海港操作对哪些点的成本产生影响。由于一艘船的成本只与相邻的两个港口位置和价值有关,所以添加港口只会影响 x x x 左右两边相邻港口范围内的船只成本

i i i 位置船距离左侧最近港口距离为 d i d_i di,位置为 l s t i lst_i lsti,右侧最近港口的价值为 v i v_i vi,位置为 n x t i nxt_i nxti

对于 x x x 右侧的船只 j j j d j d_j dj 不受影响, v j v_j vj 变为 v v v。成本变为原来的 v v j v \over v_j vjv

对于 x x x 左侧的船只 j j j v j v_j vj 不受影响, d j d_j dj 减小 n x t x − x nxt_x - x nxtxx。成本减小 v j × ( n x t x − x ) v_j \times (nxt_x - x) vj×(nxtxx)

x x x 点的成本变为 0 0 0

根据分析,港口添加操作可以使用区间加,区间乘除解决

需要注意的是 v v j v \over v_j vjv 不一定是整数,但区间和一定可以被 v j v_j vj 整除,所以可以将乘除分开。但是由于乘法懒标记容易溢出,所以要同时进行乘除操作

Code
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define i128 __int128
using ll = long long;

const int N = 3e5 + 5;

int n, m, q;

// 线段树维护区间和
// 支持区间乘除和区间加

struct Node {
	ll sum, mul, div, add;
};
class Segment {
private:
	int n;
	ll a[N];
	Node node[N << 2];
public:
	void resize (int n, ll *a) {
		this->n = n;
		for (int i = 1; i <= n; i ++) {
			this->a[i] = a[i];
		}
	}
	void push_up (int p) {
		node[p].sum = node[p << 1].sum + node[p << 1 | 1].sum;
	}
	void build (int s, int t, int p = 1) {
		node[p].add = 0;
		node[p].mul = 1;
		node[p].div = 1;
		if (s == t) {
			node[p].sum = a[s];
			return;
		}
		int mid = (s + t) >> 1;
		build(s, mid, p << 1);
		build(mid + 1, t, p << 1 | 1);
		push_up(p);
	}
	void push_down (int s, int t, int p) {
		if (s == t) return;
		int mid = (s + t) >> 1;
		int ls = p << 1, rs = p << 1 | 1;
		if (node[p].mul != 1) {
			node[ls].add = node[ls].add / node[p].div * node[p].mul;
			node[rs].add = node[rs].add / node[p].div * node[p].mul;
			node[ls].sum = node[ls].sum / node[p].div * node[p].mul;
			node[rs].sum = node[rs].sum / node[p].div * node[p].mul;
			node[ls].mul *= node[p].mul;
			node[rs].mul *= node[p].mul;
			node[ls].div *= node[p].div;
			node[rs].div *= node[p].div;
			node[p].mul = node[p].div = 1;
		}
		if (node[p].add) {
			node[ls].add += node[p].add;
			node[rs].add += node[p].add;
			node[ls].sum += node[p].add * (mid - s + 1);
			node[rs].sum += node[p].add * (t - mid);
			node[p].add = 0;
		}
	}
	void update0 (int x, int s, int t, int p = 1) {
		if (s == t) {
			node[p].sum = 0;
			return;
		}
		push_down(s, t, p);
		int mid = (s + t) >> 1;
		if (x <= mid) update0(x, s, mid, p << 1);
		else update0(x, mid + 1, t, p << 1 | 1);
		push_up(p);
	}
	void update1 (int l, int r, ll v, int s, int t, int p = 1) {
		if (l <= s && t <= r) {
			node[p].add += v;
			node[p].sum += v * (t - s + 1);
			return;
		}
		push_down(s, t, p);
		int mid = (s + t) >> 1;
		if (l <= mid) update1(l, r, v, s, mid, p << 1);
		if (mid < r) update1(l, r, v, mid + 1, t, p << 1 | 1);
		push_up(p);
	}
	void update2 (int l, int r, ll v1, ll v2, int s, int t, int p = 1) {
		if (l <= s && t <= r) {
			node[p].add = node[p].add / v2 * v1;
			node[p].mul *= v1;
			node[p].div *= v2;
			node[p].sum = node[p].sum / v2 * v1 ;
			return;
		}
		push_down(s, t, p);
		int mid = (s + t) >> 1;
		if (l <= mid) update2(l, r, v1, v2, s, mid, p << 1);
		if (mid < r) update2(l, r, v1, v2, mid + 1, t, p << 1 | 1);
		push_up(p);
	}
	ll query (int l, int r, int s, int t, int p = 1) {
		if (l <= s && t <= r) return node[p].sum;
		int mid = (s + t) >> 1;
		push_down(s, t, p);
		ll res = 0;
		if (l <= mid) res = query(l, r, s, mid, p << 1);
		if (mid < r) res += query(l, r, mid + 1, t, p << 1 | 1);
		push_up(p);
		return res;
	}
};
Segment seg;

int x[N];
ll val[N], cost[N];
int lst[N], nxt[N];

void init () {
	for (int i = 1; i <= n; i ++) {
		val[i] = 0;
	}
}

void solve() {
	cin >> n >> m >> q;
	init();
	set<int> st;
	for (int i = 1; i <= m; i ++) {
		cin >> x[i];
		st.insert(x[i]);
	}
	for (int i = 1; i <= m; i ++) {
		cin >> val[x[i]];
	}
	for (int i = 1; i <= n; i ++) {
		if (val[i]) lst[i] = i;
		else lst[i] = lst[i - 1];
	}
	for (int i = n; i >= 1; i --) {
		if (val[i]) nxt[i] = i;
		else nxt[i] = nxt[i + 1];
		cost[i] = val[lst[i]] * (nxt[i] - i);
	}
	seg.resize(n, cost);
	seg.build(1, n);
	while (q --){
		int opt;
		cin >> opt;
		if (opt == 1) {
			ll x, v;
			cin >> x >> v;
			auto it = st.lower_bound(x);
			int r = *it;
			it --;
			int l = *it;
			val[x] = v;
			st.insert(x);
			seg.update0(x, 1, n);
			if (l + 1 != x) seg.update1(l + 1, x - 1, -val[l] * (r - x), 1, n);
			if (x + 1 != r) seg.update2(x + 1, r - 1, val[x], val[l], 1, n);
		} else {
			int l, r;
			cin >> l >> r;
			cout << seg.query(l, r, 1, n) << endl;
		}
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
//	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

CF Round 909 G

题面

G. Unusual Entertainment

time limit per test: 3 seconds

memory limit per test: 256 megabytes

input: standard input

output: standard output

A tree is a connected graph without cycles.

A permutation is an array consisting of n n n distinct integers from 1 1 1 to n n n in any order. For example, [ 5 , 1 , 3 , 2 , 4 ] [5, 1, 3, 2, 4] [5,1,3,2,4] is a permutation, but [ 2 , 1 , 1 ] [2, 1, 1] [2,1,1] is not a permutation (as 1 1 1 appears twice in the array) and [ 1 , 3 , 2 , 5 ] [1, 3, 2, 5] [1,3,2,5] is also not a permutation (as n = 4 n = 4 n=4, but 5 5 5 is present in the array).

After a failed shoot in the BrMeast video, Alex fell into depression. Even his birthday did not make him happy. However, after receiving a gift from Timofey, Alex’s mood suddenly improved. Now he spent days playing with the gifted constructor. Recently, he came up with an unusual entertainment.

Alex builds a tree from his constructor, consisting of n n n vertices numbered from 1 1 1 to n n n, with the root at vertex 1 1 1. Then he writes down each integer from 1 1 1 to n n n in some order, obtaining a permutation p p p. After that, Alex comes up with q q q triples of integers l , r , x l, r, x l,r,x. For each triple, he tries to determine if there is at least one descendant of vertex x x x among the vertices p l , p l + 1 , … , p r p_l, p_{l+1}, \ldots, p_r pl,pl+1,,pr.

A vertex u u u is a descendant of vertex v v v if and only if d i s t ( 1 , v ) + d i s t ( v , u ) = d i s t ( 1 , u ) \mathrm{dist}(1, v) + \mathrm{dist}(v, u) = \mathrm{dist}(1, u) dist(1,v)+dist(v,u)=dist(1,u), where d i s t ( a , b ) \mathrm{dist}(a, b) dist(a,b) is the distance between vertices a a a and b b b. In other words, vertex v v v must be on the path from the root to vertex u u u.

Alex told Zakhar about this entertainment. Now Alex tells his friend q q q triples as described above, hoping that Zakhar can check for the presence of a descendant. Zakhar is very sleepy, so he turned to you for help. Help Zakhar answer all of Alex’s questions and finally go to sleep.

Input

The first line of the input contains a single integer t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104) — the number of test cases.

The first line of each test case contains two integers n , q n, q n,q ( 1 ≤ n , q ≤ 1 0 5 1 \le n, q \le 10^5 1n,q105) — the number of vertices in the tree and the number of questions, respectively.

Each of the next n − 1 n - 1 n1 lines contains two integers u i u_i ui and v i v_i vi ( 1 ≤ u i , v i ≤ n 1 \le u_i, v_i \le n 1ui,vin), indicating that there is an edge between vertices u i u_i ui and v i v_i vi (it is guaranteed that the resulting graph is a tree).

The next line contains n n n integers p 1 , p 2 , … , p n p_1, p_2, \dots, p_n p1,p2,,pn ( 1 ≤ p i ≤ n 1 \le p_i \le n 1pin) — the permutation p p p (it is guaranteed that each integer from 1 1 1 to n n n appears exactly once).

Then follow q q q lines describing Alex’s questions. The i i i-th line contains three integers l , r , x l, r, x l,r,x ( 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn, 1 ≤ x ≤ n 1 \le x \le n 1xn), as described in the statement.

It is guaranteed that the sum of n n n and the sum of q q q over all test cases do not exceed 1 0 5 10^5 105.

Output

For each of Alex’s questions, print “Yes” (without quotes) if the described descendant exists, otherwise print “No” (without quotes).

You can output the answer in any case (for example, the strings “yEs”, “yes”, “Yes”, and “YES” will be recognized as a positive answer).

题意

亚历克斯用他的构造函数构建了一棵树,树由 n n n 个顶点组成,顶点从 1 1 1 n n n ,根顶点位于 1 1 1 。然后,他按照一定的顺序写下从 1 1 1 n n n 的每个整数,得到一个排列组合 p p p 。之后,亚历克斯得到了整数 l , r , x l, r, x l,r,x q q q 个三元组。对于每个三元组,他都会尝试确定顶点 p l , p l + 1 , … , p r p_l, p_{l+1}, \ldots, p_r pl,pl+1,,pr 中是否至少有一个顶点 x x x 的后代。

题解

本题的关键如何判断某个点 y y y 是否是 x x x 的后代

题目本身给出了一种使用使用距离判断的方法,但是这种方法并不好用。若使用这种方式,必须处理出每两点之间的距离。

考虑当 y y y x x x 的后代时, y y y x x x 子树内。以 d f n i dfn_i dfni 表示点 i i i d f s dfs dfs 序, l o w i low_i lowi 表示 i i i 子树内最后访问点的 d f s dfs dfs 序,那么显然有 d f n x ≤ d f n y ≤ l o w x dfn_x \leq dfn_y \leq low_x dfnxdfnylowx

根据这个性质,可以对于排列 p p p 对应点的 d f s dfs dfs 序建立可持久化权值线段树来维护区间内 d f s dfs dfs 序的数量

对于查询 x , ( l , r ) x, (l, r) x,(l,r),只需要 r r r 版本和 l l l 版本区间 ( d f n x , l o w x ) (dfn_x, low_x) (dfnx,lowx) 的数量差大于 0 0 0 就表示存在 x x x 的子树

Code
#include<bits/stdc++.h>
using namespace std;

using ll = long long;
const int N = 1e5 + 10;
#define endl '\n'

int n, m;
int p[N];
vector<int> t[N];
int dfn[N], low[N], totdfn;
// 主席树查询区间内是否存在某个范围内的数
// 查询区间内是否存在x的子节点,转化为求区间内是否存在节点的dfn处于(dfn[x], low[x])之间
struct Node {
	int lson, rson;
	int val;
} node[N << 5];

#define ls(x) node[x].lson
#define rs(x) node[x].rson
int idx = 0;
void pushup(int p) {
	node[p].val = node[ls(p)].val + node[rs(p)].val;
	return ;
}
int build (int s, int t) {
	int p = ++ idx;
	if (s == t) {
		node[p].val = 0;
		return p;
	}
	int mid = (s + t) >> 1;
	ls(p) = build(s, mid);
	rs(p) = build(mid + 1, t);
	pushup(p);
	return p;
}
int update (int p, int s, int t, int x, int v) {
	int q = ++ idx;
	node[q] = node[p];
	if (s == t) {
		node[q].val += v;
		return q;
	}
	int mid = (s + t) >> 1;
	if (x <= mid) ls(q) = update(ls(q), s, mid, x, v);
	else rs(q) = update(rs(q), mid + 1, t, x, v);
	pushup(q);
	return q;
}
int query (int p, int s, int t, int l, int r) {
	if (l <= s && t <= r) {
		return node[p].val;
	}
	int mid = (s + t) >> 1;
	int res = 0;
	if (l <= mid) res = query(ls(p), s, mid, l, r);
	if (mid < r) res += query(rs(p), mid + 1, t, l, r);
	return res;
}
int rt[N];

void dfs0(int x, int fa) {
	dfn[x] = ++totdfn;
	for (auto y: t[x]) {
		if (y == fa) continue;
		dfs0(y, x);
	}
	low[x] = totdfn;
}

void init () {
	for (int i = 1; i <= n; i ++) {
		t[i].clear();
	}
	totdfn = 0;
	idx = 0;
}

void solve(){
	cin >> n >> m;
	init();
	for (int i = 1; i < n; i ++) {
		int u, v;
		cin >> u >> v;
		t[u].push_back(v);
		t[v].push_back(u);
	}
	dfs0(1, 0);
	rt[0] = build(1, n);
	for (int i = 1; i <= n; i ++) {
		cin >> p[i];
		rt[i] = update(rt[i - 1], 1, n, dfn[p[i]], 1);
	}
	while (m --) {
		int l, r, x;
		cin >> l >> r >> x;
		int res1 = query(rt[r], 1, n, dfn[x], low[x]);
		int res2 = query(rt[l - 1], 1, n, dfn[x], low[x]);
		if (res1 - res2 > 0) cout << "YES\n";
		else cout << "NO\n";
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
	cin >> _;
	while (_--) solve(); 
	return 0;
}

CF GoodBye2023 E

题面

E. Happy Life in University

time limit per test: 1 second

memory limit per test: 512 megabytes

input: standard input

output: standard output

Egor and his friend Arseniy are finishing school this year and will soon enter university. And since they are very responsible guys, they have started preparing for admission already.

First of all, they decided to take care of where they will live for the long four years of study, and after visiting the university’s website, they found out that the university dormitory can be represented as a root tree with n n n vertices with the root at vertex 1 1 1. In the tree, each vertex represents a recreation with some type of activity a i a_i ai. The friends need to choose 2 2 2 recreations (not necessarily different) in which they will settle. The guys are convinced that the more the value of the following function f ( u , v ) = d i f f ( u , l c a ( u , v ) ) ⋅ d i f f ( v , l c a ( u , v ) ) f(u, v) = diff(u, lca(u, v)) \cdot diff(v, lca(u, v)) f(u,v)=diff(u,lca(u,v))diff(v,lca(u,v)), the more fun their life will be. Help Egor and Arseniy and find the maximum value of f ( u , v ) f(u, v) f(u,v) among all pairs of recreations!

† d i f f ( u , v ) ^{\dagger} diff(u, v) diff(u,v) — the number of different activities listed on the simple path from vertex u u u to vertex v v v.

† l c a ( u , v ) ^{\dagger} lca(u, v) lca(u,v) — a vertex p p p such that it is at the maximum distance from the root and is a parent of both vertex u u u and vertex v v v.
Input

Each test consists of several test cases. The first line contains a single integer t t t ( 1 ≤ t ≤ 1 0 5 1 \le t \le 10^5 1t105) — the number of test cases. Then follows the description of the test cases.

The first line of each test case contains a single integer n n n ( 1 ≤ n ≤ 3 ⋅ 1 0 5 1 \le n \le 3 \cdot 10^{5} 1n3105).

The second line of each test case contains n − 1 {n - 1} n1 integers p 2 , p 3 , … , p n p_2, p_3, \ldots,p_n p2,p3,,pn ( 1 ≤ p i ≤ i − 1 1 \le p_i \le i - 1 1pii1), where p i p_i pi — the parent of vertex i i i.

The third line of each test case contains n {n} n integers a 1 , a 2 , … , a n a_1, a_2, \ldots,a_n a1,a2,,an ( 1 ≤ a i ≤ n 1 \le a_i \le n 1ain), where a i a_i ai — the number of the activity located at vertex i i i.

It is guaranteed that the sum of n n n over all test cases does not exceed 3 ⋅ 1 0 5 3 \cdot 10^5 3105.

Output

For each test case, output the maximum value of f ( u , v ) f(u, v) f(u,v) for all pairs of recreations ( u , v ) (u, v) (u,v).

题意

大学宿舍可以表示为一棵根树,树上有 n n n 个顶点,根位于顶点 1 1 1 。在这棵树上,每个顶点都代表一个娱乐活动 a i a_i ai 。朋友们需要选择 2 2 2 个娱乐项目(不一定不同),并在其中定居。他们深信,以下函数 f ( u , v ) = d i f f ( u , l c a ( u , v ) ) ⋅ d i f f ( v , l c a ( u , v ) ) f(u, v) = diff(u, lca(u, v)) \cdot diff(v, lca(u, v)) f(u,v)=diff(u,lca(u,v))diff(v,lca(u,v)) 的值越大,他们的生活就越有趣。请帮助 Egor 和 Arseniy 在所有娱乐项目中找出 f ( u , v ) f(u, v) f(u,v) 的最大值!

† d i f f ( u , v ) ^{\dagger} diff(u, v) diff(u,v) - 从顶点 u u u 到顶点 v v v 的简单路径上列出的不同活动的数量。

题解

考虑这样一种枚举策略,枚举 x = l c a ( u , v ) x = lca(u, v) x=lca(u,v),在确定 l c a lca lca max ⁡ ( f ( u , v ) ) \max(f(u, v)) max(f(u,v)) 就是在 x x x 子树内找到 d i f f ( 1 , u ) diff(1, u) diff(1,u) 的最大值和次大值之积

那么如何快速的维护 x x x 子树内 d i f f ( 1 , u ) diff(1, u) diff(1,u) 的最大值?

首先还是利用 d f s dfs dfs 序的性质,子树 x x x 内的节点 d f s dfs dfs 序连续,那么就可以将维护子树最大值转变为在线段树上维护区间最大值。

此时需要考虑在 x x x 改变时 d i f f diff diff 的变化

y y y 表示 x x x 的一个子节点,当枚举的 l c a lca lca x − > y x -> y x>y 时,根路径上有与 x x x 类型相同的节点 d i f f diff diff 不变,其余节点 d i f f diff diff 1 1 1

可以通过将 x x x 子树全部减 1 1 1,然后再把靠近 x x x (到 x x x 的路径上没有与 x x x 类型相同的其他节点)的相同类型子树加 1 1 1

可以看到,这种策略每个节点只会进行两次区间加操作

对于每个节点的后继同类型节点可以和 d f s dfs dfs 序的处理以及线段树初始化过程中一起执行

Code
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
using ll = long long;

const int N = 3e5 + 5;

struct Node {
	ll tag, mx;
};

// 利用dfn将子树转化为序列来进行子树加减和子树最值查询

class Segment {
private:
	int n;
	int a[N];
	Node node[N << 2];
public:
	void resize (int n) {
		this->n = n;
		for (int i = 1; i <= n; i ++) a[i] = 0;
	}
	void push_up (int p) {
		int ls = p << 1, rs = p << 1 | 1;
		node[p].mx = max(node[ls].mx, node[rs].mx);
	}
	void build (int s, int t, int p) {
		node[p].tag = 0;
		if (s == t) {
			node[p].mx = a[s];
			return;
		}
		int mid = (s + t) >> 1;
		build(s, mid, p << 1);
		build(mid + 1, t, p << 1 | 1);
		push_up(p);
	}
	void push_down (int s, int t, int p) {
		if (node[p].tag) {
			int ls = p << 1, rs = p << 1 | 1;
			node[ls].mx += node[p].tag;
			node[ls].tag += node[p].tag;
			node[rs].mx += node[p].tag;
			node[rs].tag += node[p].tag;
			node[p].tag = 0;
		}
	}
	void update (int s, int t, int l, int r, int p, ll v) {
		if (l <= s && t <= r) {
			node[p].tag += v;
			node[p].mx += v;
			return;
		}
		push_down(s, t, p);
		int mid = (s + t) >> 1;
		if (l <= mid) update(s, mid, l, r, p << 1, v);
		if (mid < r) update(mid + 1, t, l, r, p << 1 | 1, v);
		push_up(p);
	}
	ll query (int s, int t, int l, int r, int p) {
		if (l <= s && t <= r) return node[p].mx;
		push_down(s, t, p);
		int mid = (s + t) >> 1;
		ll res = 0;
		if (l <= mid) res = query(s, mid, l, r, p << 1);
		if (mid < r) res = max(res, query(mid + 1, t, l, r, p << 1 | 1));
		push_up(p);
		return res;
	}
};
Segment seg;

int n;
int p[N], a[N];
int dfn[N], low[N], totdfn;
int pre[N], cnt;
ll dep[N], ans;
vector<int> t[N], nxt[N];

void init () {
	for (int i = 1; i <= n; i ++) {
		p[i] = i;
		t[i].clear();
		nxt[i].clear();
		pre[i] = 0;
	}
	totdfn = 0;
	cnt = 0;
	ans = 1;
}

void dfs1 (int x) {
	dfn[x] = ++ totdfn;
	int now = pre[a[x]];
	if (!now) cnt ++;
	else nxt[now].push_back(x);
	pre[a[x]] = x;
	seg.update(1, n, dfn[x], dfn[x], 1, cnt);
	for (auto y: t[x]) {
		if (y == p[x]) continue;
		dfs1(y);
	}
	pre[a[x]] = now;
	if (!now) cnt --;
	low[x] = totdfn;
}

void dfs2 (int x) {
	ll mx1 = 1;
	for (auto y: t[x]) {
		ll mx2 = seg.query(1, n, dfn[y], low[y], 1);
		assert(mx2 != 0);
		ans = max(ans, mx1 * mx2);
		mx1 = max(mx1, mx2);
	}
	seg.update(1, n, dfn[x], low[x], 1, -1);
	for (auto y: nxt[x]) {
		seg.update(1, n, dfn[y], low[y], 1, 1);
	}
	for (auto y: t[x]) {
		if (y == p[x]) continue;
		dfs2(y);
	}
}

void solve() {
	cin >> n;
	init();
	seg.resize(n);
	seg.build(1, n, 1);
	for (int i = 2; i <= n; i ++) {
		cin >> p[i];
		t[p[i]].push_back(i);
	}
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}
	dfs1(1);
	dfs2(1);
	cout << ans << endl;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

CF Round 881 F2

题面

F2. Omsk Metro (hard version)

time limit per test: 2 seconds

memory limit per test: 512 megabytes

input: standard input

output: standard output

This is the hard version of the problem. The only difference between the simple and hard versions is that in this version u u u can take any possible value.

As is known, Omsk is the capital of Berland. Like any capital, Omsk has a well-developed metro system. The Omsk metro consists of a certain number of stations connected by tunnels, and between any two stations there is exactly one path that passes through each of the tunnels no more than once. In other words, the metro is a tree.

To develop the metro and attract residents, the following system is used in Omsk. Each station has its own weight x ∈ { − 1 , 1 } x \in \{-1, 1\} x{1,1}. If the station has a weight of − 1 -1 1, then when the station is visited by an Omsk resident, a fee of 1 1 1 burle is charged. If the weight of the station is 1 1 1, then the Omsk resident is rewarded with 1 1 1 burle.

Omsk Metro currently has only one station with number 1 1 1 and weight x = 1 x = 1 x=1. Every day, one of the following events occurs:

  • A new station with weight x x x is added to the station with number v i v_i vi, and it is assigned a number that is one greater than the number of existing stations.
  • Alex, who lives in Omsk, wonders: is there a subsegment † \dagger (possibly empty) of the path between vertices u u u and v v v such that, by traveling along it, exactly k k k burles can be earned (if KaTeX parse error: Expected 'EOF', got '&' at position 3: k &̲lt; 0, this means that k k k burles will have to be spent on travel). In other words, Alex is interested in whether there is such a subsegment of the path that the sum of the weights of the vertices in it is equal to k k k. Note that the subsegment can be empty, and then the sum is equal to 0 0 0.

You are a friend of Alex, so your task is to answer Alex’s questions.

† \dagger Subsegment — continuous sequence of elements.

Input

The first line contains a single number t t t ( 1 ≤ t ≤ 1 0 4 1 \leq t \leq 10^4 1t104) — the number of test cases.

The first line of each test case contains the number n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \leq n \leq 2 \cdot 10^5 1n2105) — the number of events.

Then there are n n n lines describing the events. In the i i i-th line, one of the following options is possible:

  • First comes the symbol “+” (without quotes), then two numbers v i v_i vi and x i x_i xi ( x i ∈ { − 1 , 1 } x_i \in \{-1, 1\} xi{1,1}, it is also guaranteed that the vertex with number v i v_i vi exists). In this case, a new station with weight x i x_i xi is added to the station with number v i v_i vi.
  • First comes the symbol “?” (without quotes), and then three numbers u i u_i ui, v i v_i vi, and k i k_i ki ( − n ≤ k i ≤ n -n \le k_i \le n nkin). It is guaranteed that the vertices with numbers u i u_i ui and v i v_i vi exist. In this case, it is necessary to determine whether there is a subsegment (possibly empty) of the path between stations u i u_i ui and v i v_i vi with a sum of weights exactly equal to k i k_i ki.

It is guaranteed that the sum of n n n over all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105.

Output

For each of Alex’s questions, output “Yes” (without quotes) if the subsegment described in the condition exists, otherwise output “No” (without quotes).

You can output the answer in any case (for example, the strings “yEs”, “yes”, “Yes” and “YES” will be recognized as a positive answer).

题意

众所周知,鄂木斯克是白俄罗斯的首都。与其他首都一样,鄂木斯克也有发达的地铁系统。鄂木斯克地铁由一定数量的地铁站组成,这些地铁站通过隧道相连,在任何两个地铁站之间都有一条路径,这条路径穿过每个隧道的次数都不超过一次。换句话说,地铁就是一棵树。

为了发展地铁并吸引居民,鄂木斯克采用了以下系统。每个车站都有自己的权重 x ∈ { − 1 , 1 } x \in \{-1, 1\} x{1,1} 。如果车站的权重是 − 1 -1 1 ,那么当鄂木斯克居民访问该车站时,就会收取 1 1 1 布尔的费用。如果车站的重量是 1 1 1 ,那么鄂木斯克居民就会得到 1 1 1 布尔的奖励。

鄂木斯克地铁目前只有一个编号为 1 1 1 、重量为 x = 1 x = 1 x=1 的车站。每天都会发生以下事件之一:

  • 在编号为 v i v_i vi 的车站上增加了一个权重为 x x x 的新车站,它的编号比现有车站的编号多一个。
  • 住在鄂木斯克的亚历克斯想知道:顶点 u u u v v v 之间的路径上是否存在一个子段 † \dagger (可能是空),沿着这个子段旅行可以获得恰好 k k k 布尔(如果是 k < 0 k \lt 0 k<0 ,则意味着需要花费 k k k 布尔旅行)。换句话说,亚历克斯感兴趣的是,是否存在这样一段子路径,其中各顶点的权重之和等于 k k k 。注意,这个子段可以是空的,那么总和就等于 0 0 0

你是亚历克斯的朋友,所以你的任务是回答亚历克斯的问题。

† \dagger 子段 - 元素的连续序列。

题解

本题的关键在于如何确定某段 { 1 , − 1 } \{1, -1\} {1,1} 序列中是否存在一个子段和为 k k k

考虑这样一种情况,若 ( l , r ) (l, r) (l,r) 的区间和为 x x x ( l , r − 1 ) ; ( l , r + 1 ) ; ( l − 1 , r ) ; ( l + 1 , r ) (l, r - 1); (l, r + 1); (l - 1, r); (l + 1, r) (l,r1);(l,r+1);(l1,r);(l+1,r) 的区间和一定属于 { x − 1 , x + 1 } \{x - 1, x + 1\} {x1,x+1}

那么,对于任意路径,其子段和的取值一定是从最小和到最大和全部都可取

所以需要维护的内容就是树上路径权值和的最小最大值

首先将操作离线,建出树

为了维护树上路径信息,需要进行重链剖分将树分为若干链,然后考虑如何通过线段树维护区间和的最值

对于最大区间和,需要维护前后缀最大和 l m x p lmx_p lmxp r m x p rmx_p rmxp 以及区间和 s u m p sum_p sump,最大子区间和 m x p mx_p mxp

l s , r s ls, rs ls,rs 表示 p p p 的左右子节点

  • s u m p = s u m l s + s u m r s sum_p = sum_{ls} + sum_{rs} sump=sumls+sumrs
  • l m x p = max ⁡ ( s u m l s + l m x r s , l m x l s ) lmx_p = \max(sum_{ls} + lmx_{rs}, lmx_{ls}) lmxp=max(sumls+lmxrs,lmxls)
  • r m x p = max ⁡ ( r m x l s + s u m r s , r m x r s ) rmx_p = \max(rmx_{ls} + sum_{rs}, rmx_{rs}) rmxp=max(rmxls+sumrs,rmxrs)
  • m x p = max ⁡ ( r m x l s + l m x r s , m x l s , m x r s ) mx_p = \max(rmx_{ls} + lmx_{rs}, mx_{ls}, mx_{rs}) mxp=max(rmxls+lmxrs,mxls,mxrs)

需要注意,在查询 u , v u,v u,v 路径时, l c a ( u , v ) lca(u, v) lca(u,v) 到两节点的路径上左端点都是靠近 l c a lca lca 这边,所以对于当前点的信息合并与线段树内以及单侧路径上有细微区别

Code
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
#define endl '\n'
const int N = 2e5 + 10;
const ll mod = 1e9 + 7;

// 树链剖分+线段树
// 先离线建树,线段树维护区间最大和以及区间最小和
// 具体维护方式为merge函数部分
// 查询时由于lca两侧向下的线段都是lca这端为左端点,所以统计答案和merge的策略有所不同

int n, m;
vector<int> t[N];
int a[N];
int dfn[N], tp[N], totdfn, dep[N], sz[N], hson[N], pos[N], f[N];

void init () {
	totdfn = 0;
	for (int i = 1; i <= n; i ++) {
		t[i].clear();
	}
	n = 1;
	a[1] = 1;
}

struct str {
	int u, v, k;
};

void dfs0 (int x, int fa) {
	f[x] = fa;
	sz[x] = 1;
	dep[x] = dep[fa] + 1;
	hson[x] = 0;
	for (auto y: t[x]) {
		if (y == fa) continue;
		dfs0(y, x);
		sz[x] += sz[y];
		if (sz[y] > sz[hson[x]]) hson[x] = y;
	}
}
void dfs1 (int x, int fa, int top) {
	tp[x] = top;
	dfn[x] = ++ totdfn;
	pos[totdfn] = x;
	if (hson[x]) dfs1(hson[x], x, top);
	for (auto y: t[x]) {
		if (y == fa || y == hson[x]) continue;
		dfs1(y, x, y);
	}
}

struct Node {
	int sum, lmx, rmx, lmi, rmi, mx, mi;
	Node(){}
	Node(int x) {
		sum = x;
		mx = lmx = rmx = max(0, x);
		mi = lmi = rmi = min(0, x);
	}
} node[N << 2];
Node merge (Node x, Node y) {
	Node res;
	res.sum = x.sum + y.sum;
	res.lmx = max(x.lmx, x.sum + y.lmx);
	res.rmx = max(y.rmx, y.sum + x.rmx);
	res.mx = max(x.mx, y.mx);
	res.mx = max(res.mx, x.rmx + y.lmx);
	res.lmi = min(x.lmi, x.sum + y.lmi);
	res.rmi = min(y.rmi, y.sum + x.rmi);
	res.mi = min(x.mi, y.mi);
	res.mi = min(res.mi, x.rmi + y.lmi);
	return res;
}
void push_up (int p) {
	int ls = p << 1;
	int rs = p << 1 | 1;
	node[p] = merge(node[ls], node[rs]);
}
void build (int s, int t, int p) {
	if (s == t) {
		node[p] = Node(a[pos[s]]);
		return;
	}
	int mid = (s + t) >> 1;
	build(s, mid, p << 1);
	build(mid + 1, t, p << 1 | 1);
	push_up(p);
}
Node query (int s, int t, int l, int r, int p) {
	int ls = p << 1;
	int rs = p << 1 | 1;
	if (l <= s && t <= r) return node[p];
	int mid = (s + t) >> 1;
	if (l > mid) return query(mid + 1, t, l, r, rs);
	if (r <= mid) return query(s, mid, l, r, ls);
	Node res1 = query(s, mid, l, r, ls);
	Node res2 = query(mid + 1, t, l, r, rs);
	Node res = merge(res1, res2);
	return res;
}

bool check (int x, int y, int k) {
	Node xans = Node(0), yans = Node(0);
	while (tp[x] != tp[y]) {
		if (dep[tp[x]] >= dep[tp[y]]) {
			Node tmp = query(1, n, dfn[tp[x]], dfn[x], 1);
			xans = merge(tmp, xans);
			x = f[tp[x]];
		} else {
			Node tmp = query(1, n, dfn[tp[y]], dfn[y], 1);
			yans = merge(tmp, yans);
			y = f[tp[y]];
		}
	}
	if (dep[x] >= dep[y]) {
		Node tmp = query(1, n, dfn[y], dfn[x], 1);
		xans = merge(tmp, xans);
	} else {
		Node tmp = query(1, n, dfn[x], dfn[y], 1);
		yans = merge(tmp, yans);
	}
	int mx = max(xans.mx, yans.mx);
	mx = max(mx, xans.lmx + yans.lmx);
	int mi = min(xans.mi, yans.mi);
	mi = min(mi, xans.lmi + yans.lmi);
	if (mi <= k && k <= mx) return 1;
	return 0;
}

void solve () {
	cin >> m;
	init();
	vector<str> query;
	for (int i = 1; i <= m; i ++) {
		char opt;
		cin >> opt;
		if (opt == '+') {
			int u, x;
			cin >> u >> x;
			t[u].push_back(++ n);
			t[n].push_back(u);
			a[n] = x;
		} else {
			int u, v, k;
			cin >> u >> v >> k;
			query.push_back({u, v, k});
		}
	}
	dfs0(1, 0);
	dfs1(1, 0, 1);
	build(1, n, 1);
	for (auto q: query) {
		int u = q.u, v = q.v, k = q.k;
		if (k == 0 || check(u, v, k)) cout << "YES\n";
		else cout << "NO\n";
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int _ = 1;
	cin >> _;
	while (_--){
		solve();
	}
	return 0;
}

CF Round 474 F

题面

F. Pathwalks

time limit per test: 1 second

memory limit per test: 256 megabytes

input: standard input

output: standard output

You are given a directed graph with n n n nodes and m m m edges, with all edges having a certain weight.

There might be multiple edges and self loops, and the graph can also be disconnected.

You need to choose a path (possibly passing through same vertices multiple times) in the graph such that the weights of the edges are in strictly increasing order, and these edges come in the order of input. Among all such paths, you need to find the the path that has the maximum possible number of edges, and report this value.

Please note that the edges picked don’t have to be consecutive in the input.

Input

The first line contains two integers n n n and m ( 1   ≤   n ≤   100000 , 1   ≤   m   ≤   100000 ) m (1 \leq n\leq 100000,1 \leq m \leq 100000) m(1 n 100000,1 m 100000) — the number of vertices and edges in the graph, respectively.

m m m lines follows.

The i − t h i-th ith of these lines contains three space separated integers a i , b i a_i, b_i ai,bi and w i ( 1   ≤   a i ,   b i   ≤   n , 0   ≤   w i   ≤   100000 ) w_i (1 \leq a_i, b_i \leq n, 0 \leq w_i \leq 100000) wi(1 ai,bin,0 wi 100000), denoting an edge from vertex a i a_i ai to vertex b i b_i bi having weight w i w_i wi

Output

Print one integer in a single line — the maximum number of edges in the path.

题意

给出一个 n n n 个节点 m m m 条边的有向图,每条边有一个边权。可能有重边和自环,不保证图联通。

输出边权递增并且输入顺序也递增的最长路径

题解

考虑记录对于每个节点作为结尾时,最后一段边权为不同值时的最大长度,以 d p x , i dp_{x,i} dpx,i 表示

按照输入顺序处理,将 d p v , w dp_{v,w} dpv,w 更新为 max ⁡ i < w { d p u , i } + 1 \max _{i \lt w}\{dp_{u, i} \} + 1 maxi<w{dpu,i}+1

这一更新过程可以通过在每一棵树建立线段树维护 d p dp dp 区间最大值来优化

Code
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
#define endl '\n'
const int N = 1e5 + 10;

// 每个点建一棵树,记录以当前点结尾时,最后一段边权为不同值的最大长度
// 统计答案时按照输入顺序,查询起点u的边权小于w的最大长度 now,并更新v的w节点权值为now + 1
int n, m;
struct Node {
	int lson, rson;
	int val;
} node[N << 5];
#define ls(x) node[x].lson
#define rs(x) node[x].rson
int idx = 0;
int add (int p, int s, int t, int x, int v) {
	int q = ++ idx;
	node[q] = node[p];
	if (s == t) {
		node[q].val = v;
		return q;
	}
	int mid = (s + t) >> 1;
	if (x <= mid) ls(q) = add(ls(q), s, mid, x, v);
	else rs(q) = add(rs(q), mid + 1, t, x, v);
	node[q].val = max(node[ls(q)].val, node[rs(q)].val);
	return q;
}
int query (int p, int s, int t, int l, int r) {
	if (l <= s && t <= r) return node[p].val;
	int mid = (s + t) >> 1;
	int res = 0;
	if (l <= mid) res = query(ls(p), s, mid, l, r);
	if (mid < r) res = max(res, query(rs(p), mid + 1, t, l, r));
	return res;
}
int rt[N];
void solve () {
	cin >> n >> m;
	int ans = 0;
	for (int i = 1; i <= m; i ++) {
		int u, v, w;
		cin >> u >> v >> w;
		int now = 0;
		if (w > 0 && rt[u]) now = query(rt[u], 0, N, 0, w - 1);
		ans = max(ans, now + 1);
		rt[v] = add(rt[v], 0, N, w, now + 1);
	}
	cout << ans << endl;
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int _ = 1;
//	cin >> _;
	while (_--){
		solve();
	}
	return 0;
}

CF Round 406 B

题面

B. Legacy

time limit: per test2 seconds

memory limit: per test256 megabytes

input: standard input

output: standard output

Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them.

There are n planets in their universe numbered from 1 1 1 to n n n. Rick is in planet number s s s (the earth) and he doesn’t know where Morty is. As we all know, Rick owns a portal gun. With this gun he can open one-way portal from a planet he is in to any other planet (including that planet). But there are limits on this gun because he’s still using its free trial.

By default he can not open any portal by this gun. There are q q q plans in the website that sells these guns. Every time you purchase a plan you can only use it once but you can purchase it again if you want to use it more.

Plans on the website have three types:

  1. With a plan of this type you can open a portal from planet v v v to planet u u u.
  2. With a plan of this type you can open a portal from planet v v v to any planet with index in range [ l ,   r ] [l, r] [l,r].
  3. With a plan of this type you can open a portal from any planet with index in range [ l ,   r ] [l, r] [l,r] to planet v v v.

Rick doesn’t known where Morty is, but Unity is going to inform him and he wants to be prepared for when he finds and start his journey immediately. So for each planet (including earth itself) he wants to know the minimum amount of money he needs to get from earth to that planet.

Input

The first line of input contains three integers n n n, q q q and s ( 1   ≤   n ,   q   ≤   1 0 5 , 1   ≤   s   ≤   n ) s (1 \leq n, q \leq 10^5, 1 \leq s \leq n) s(1 n,q 105,1 sn) — number of planets, number of plans and index of earth respectively.

The next q q q lines contain the plans. Each line starts with a number t, type of that plan ( 1   ≤   t   ≤   3 ) (1 \leq t \leq 3) (1 t 3). If t   =   1 t = 1 t= 1 then it is followed by three integers v v v, u u u and w w w where w w w is the cost of that plan ( 1   ≤   v ,   u   ≤   n , 1   ≤   w   ≤   1 0 9 ) (1 \leq v, u \leq n, 1 \leq w \leq 10^9) (1 v,un,1 w 109). Otherwise it is followed by four integers v v v, l l l, r r r and w w w where w w w is the cost of that plan ( 1   ≤   v   ≤   n , 1   ≤   l   ≤   r   ≤   n , 1   ≤   w   ≤   1 0 9 ) (1 \leq v \leq n, 1 \leq l \leq r \leq n, 1 \leq w \leq 10^9) (1 vn,1 lrn,1 w 109).

Output

In the first and only line of output print n n n integers separated by spaces. i i i-th of them should be minimum money to get from earth to i i i-th planet, or   −   1 - 1  1 if it’s impossible to get to that planet.

题意

n n n 个点,三种有向边添加方式

  1. v v v u u u 边权为 w w w
  2. v v v ( l , r ) (l, r) (l,r) 节点边权为 w w w
  3. ( l , r ) (l, r) (l,r) 节点到 u u u 边权为 w w w

s s s 到其它点的最短路

题解

由于 2 , 3 2, 3 2,3 两种加边方案,需要采取线段树优化建图

将所有节点分为出点和入点,对于出点和入点分别建线段树,树边权为 0 0 0。出点边自底向上,入点边自顶向下。

对于出入点的叶节点,在相同编号的叶节点之间建双向边,边权为 0 0 0

OhNqp.png

  • 对于点到点,直接从出点连向入点
  • 对于点到区间,从出点连向入点的对应区间。由于线段树上一个区间可以使用 l o g n logn logn 个子区间表示,所以复杂度可以得到优化
  • 对于区间到点,从出点区间连向入点
  • 对于区间到区间,需要先建立虚点,然后从出点连向虚点,再从虚点连向入点。需要注意,边权只在虚点前或虚点后一侧,另一侧为 0 0 0

建完图之后从 s s s 的出点为源点跑最短路,各点入点的距离就是到 s s s 的最短距离

Code
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define i128 __int128
using ll = long long;

const int N = 1e5 + 5;

vector<pair<int, ll>> g[N * 60];
int inid[N], outid[N];

// 线段树优化建图
struct Node {
	int lson, rson;
	int l, r;
};
#define ls(x) node[x].lson
#define rs(x) node[x].rson
class Segment_graph {
private:
	Node node[N << 2];
	int cnt = 0;
public:
	int build (int s, int t, int type) {
		int p = ++ cnt;
		node[p].l = s;
		node[p].r = t;
		if (s == t) {
			if (type) { // in
				inid[s] = p;
				g[p].emplace_back(outid[s], 0);
				g[outid[s]].emplace_back(p, 0);
			} else {
				outid[s] = p;
			}
			return p;
		}
		int mid = (s + t) >> 1;
		ls(p) = build(s, mid, type);
		rs(p) = build(mid + 1, t, type);
		if (type) { // in
			g[p].emplace_back(ls(p), 0);
			g[p].emplace_back(rs(p), 0);
		} else { // out
			g[ls(p)].emplace_back(p, 0);
			g[rs(p)].emplace_back(p, 0);
		}
		return p;
	}
	void add1 (int u, int v, ll w) { // 点到点
		g[outid[u]].emplace_back(inid[v], w);
	}
	void add2 (int u, int l, int r, int p, ll w) { // 点到区间
		if (l <= node[p].l && node[p].r <= r) {
			g[outid[u]].emplace_back(p, w);
			return;
		}
		if (node[ls(p)].r >= l) add2(u, l, r, ls(p), w);
		if (node[rs(p)].l <= r) add2(u, l, r, rs(p), w);
	}
	void add3 (int u, int l, int r, int p, ll w) { // 区间到点
		if (l <= node[p].l && node[p].r <= r) {
			g[p].emplace_back(inid[u], w);
			return;
		}
		if (node[ls(p)].r >= l) add3(u, l, r, ls(p), w);
		if (node[rs(p)].l <= r) add3(u, l, r, rs(p), w);
	}
};
Segment_graph seg;

ll dis[N << 6];
bool vis[N << 6];
void dijkstra (int s) {
	memset(dis, 0x3f, sizeof dis);
	dis[s] = 0;
	priority_queue<pair<ll, int>> q;
	q.emplace(0, s);
	while (!q.empty()) {
		int x = q.top().second;
		q.pop();
		if (vis[x]) continue;
		vis[x] = 1;
		for (auto &[y, w]: g[x]) {
			if (dis[y] > dis[x] + w) {
				dis[y] = dis[x] + w;
				q.emplace(-dis[y], y);
			}
		}
	}
}

int n, m, s;

void solve() {
	cin >> n >> m >> s;
	int outrt = seg.build(1, n, 0);
	int inrt = seg.build(1, n, 1);
	while (m --) {
		int opt;
		cin >> opt;
		if (opt == 1) {
			int u, v, w;
			cin >> u >> v >> w;
			seg.add1(u, v, w);
		}
		if (opt == 2) {
			int u, l, r, w;
			cin >> u >> l >> r >> w;
			seg.add2(u, l, r, inrt, w);
		}
		if (opt == 3) {
			int v, l, r, w;
			cin >> v >> l >> r >> w;
			seg.add3(v, l, r, outrt, w);
		}
	}
	dijkstra(outid[s]);
	for (int i = 1; i <= n; i ++) {
		if (dis[inid[i]] == dis[0]) cout << -1 << " \n"[i == n];
		else cout << dis[inid[i]] << " \n"[i == n];
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int _ = 1;
//	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值