2022“杭电杯”中国大学生算法设计超级联赛(2)

Static Query on Tree

给定一颗内向树,q个询问,每次给定3个点集A、B、C,有多少个x,满足\(\exists a \in A ,\exists b \in B, \exists c \in C\)使得a能走到x,b能走到x,x能走到c。
若把点集A中的点到根都覆盖颜色0,点集B中得点到根都覆盖颜色1,只需求C中每个点子树中2种颜色都有的点的个数(且不能重复计算)。用树剖+线段树。
考虑线段树节点维护:tag[2]表示颜色0、颜色1的覆盖标记,cnt[0]表示只覆盖了颜色0的点个数(cnt[1]同理),sum表示两种颜色都有的点的个数。
考虑给节点p覆盖颜色x,则显然tag[p][x]=1,cnt[p][x]+=原先无颜色的点个数(可以通过总长-cnt[p][0]-cnt[p][1]+sum[p]求得),sum[p]+=原先只有颜色(1-x)的点的个数,cnt[p][!x]=0
线段树询问使要打个标记,把计算过的sum都赋成0。
SAVE数组用于还原线段树来做多组数据

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int gi() {
	int x = 0; char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - 48, c = getchar();
	return x;
}
namespace seg {
	vector<int> SAVE;
	int tag[N << 2][2], cnt[N << 2][2], sum[N << 2], tag1[N << 2];
	void addtag(int p, int l, int r, int x) {
		SAVE.push_back(p);
		int zero = r - l + 1 - (cnt[p][0] + cnt[p][1] - sum[p]);
		tag[p][x] = 1;
		cnt[p][x] += zero;
		sum[p] += cnt[p][!x];
		cnt[p][!x] = 0;
	}
	void up(int p) {
		for (int i = 0; i < 2; ++i) cnt[p][i] = cnt[p << 1][i] + cnt[p << 1 | 1][i];
		sum[p] = sum[p << 1] + sum[p << 1 | 1];
	}
	void down(int p, int l, int r, int mid) {
		SAVE.push_back(p);
		for (int i = 0; i < 2; ++i)
			if (tag[p][i]) {
				addtag(p << 1, l, mid, i);
				addtag(p << 1 | 1, mid + 1, r, i);
				tag[p][i] = 0;
			}
	}
	void mdf(int p, int l, int r, int L, int R, int c) {
		if (l > R || L > r || L > R) return;
		if (L <= l && r <= R) {
			addtag(p, l, r, c);
			return;
		}
		int mid = l + r >> 1;
		down(p, l, r, mid);
		mdf(p << 1, l, mid, L, R, c);
		mdf(p << 1 | 1, mid + 1, r, L, R, c);
		up(p);
	}
	int qry(int p, int l, int r, int L, int R, int flag) {
		flag |= tag1[p];
		if (l > R || L > r || L > R || flag) return 0;
		if (L <= l && r <= R) {
			SAVE.push_back(p);
			int tmp = sum[p];
			sum[p] = 0;
			tag1[p] = 1;
			return tmp;
		}
		int mid = l + r >> 1;
		down(p, l, r, mid);
		int tmp = qry(p << 1, l, mid, L, R, flag) + qry(p << 1 | 1, mid + 1, r, L, R, flag);
		sum[p] = sum[p << 1] + sum[p << 1 | 1];
		return tmp;
	}
	void clear() {
		for (int x : SAVE) tag[x][0] = tag[x][1] = cnt[x][0] = cnt[x][1] = sum[x] = tag1[x] = 0;
		SAVE.clear();
	}
}
int siz[N], fa[N], son[N], top[N], n, q, timer, dfn[N];
vector<int> G[N];
void dfs1(int u) {
	siz[u] = 1;
	for (int v : G[u]) {
		fa[v] = u;
		dfs1(v);
		siz[u] += siz[v];
		if (siz[v] > siz[son[u]]) son[u] = v;
	}
}
void dfs2(int u, int t) {
	dfn[u] = ++timer;
	top[u] = t;
	if (!son[u]) return;
	dfs2(son[u], t);
	for (int v : G[u]) {
		if (v == son[u]) continue;
		dfs2(v, v);
	}
}
void add(int x, int c) {
	while (x) {
//		cerr << "#" << dfn[top[x]] << " " << dfn[x] << " " << c << endl;
		seg::mdf(1, 1, n, dfn[top[x]], dfn[x], c);
		x = fa[top[x]];
	}
}
int ask(int x) {
//	cerr << "#" << dfn[x] << " " << dfn[x] + siz[x] - 1 << endl;
	return seg::qry(1, 1, n, dfn[x], dfn[x] + siz[x] - 1, 0);
}
int main() {
//	freopen("a.in", "r", stdin);
//	freopen("a.out", "w", stdout);
	int T = gi();
	while (T--) {
		n = gi(), q = gi();
		for (int i = 1; i <= n; ++i) G[i].clear();
		timer = 0;
		for (int i = 2; i <= n; ++i) G[gi()].push_back(i);
		dfs1(1);
		dfs2(1, 1);
		while (q--) {
			seg::clear();
			int sa = gi(), sb = gi(), sc = gi();
			while (sa--) add(gi(), 0);
			while (sb--) add(gi(), 1);
			int ans = 0;
			while (sc--) ans += ask(gi());
			printf("%d\n", ans);
		}
	}
	return 0;
}

Copy

给定一个数组,操作1为把[l,r]区间重复一遍放在r+1开始的位置,比如[1,2,3,4,5]做一个操作(1,2,3),变成[1,2,3,2,3,4,5]。
操作2询问位置x的值,要求所有操作2的答案的异或和。
考虑倒着做,用bitset表示每一位的是否对答案有贡献,[l,r]只需把r+1~n都往前移动len位。

#include <bits/stdc++.h>
using namespace std;
bitset<100005> A, B;
int n, Q, a[100005];
struct QQQ {
	int o, l, r;
} q[100005];
int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &Q);
		for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
		for (int i = 1; i <= Q; ++i) {
			scanf("%d", &q[i].o);
			if (q[i].o == 1) scanf("%d%d", &q[i].l, &q[i].r);
			else scanf("%d", &q[i].l);
		}
		A.reset();
		int ans = 0;
		for (int i = Q; i >= 1; --i) {
			if (q[i].o == 2) A.flip(q[i].l);
			else {
				B = A >> q[i].r + 1 << q[i].r + 1;
				A ^= B;
				B >>= q[i].r - q[i].l + 1;
				A ^= B;
			}
		}
		for (int i = 1; i <= n; ++i)
			if (A[i])
				ans ^= a[i];
		printf("%d\n", ans);
	}
	return 0;
}

Snatch Groceries

给n个区间,区间从左到右,直到有区间重复

#include <bits/stdc++.h>
using namespace std;
pair<int, int> a[100005];
int n;
int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)
			scanf("%d%d", &a[i].first, &a[i].second);
		sort(a + 1, a + n + 1);
		int ans = 0;
		for (int i = 1; i < n; ++i) {
			if (a[i].second < a[i + 1].first) ++ans;
			else break;
			if (i == n - 1) ++ans;
		}
		cout << ans << endl;
	}
	return 0;
}
posted @ 2022-07-22 23:09  chenyilei  阅读(64)  评论(0编辑  收藏  举报