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;
}