codeforces Ethflow Round 1, div1+2, A-E2
目录
cf2062
A
- 显然一个一个把 1 转成 0 最优
B
猜想每个点都要满足: 向左/向右走到尽头再返回这个点时, 时钟还没结束
for (int i = 1; i <= n; i++) { int x = a[i] - 1 >> 1; if (i - x > 1 || i + x < n) { cout << "NO\n"; return; } }
C
- 猜想直接模拟一直差分, 每次数组的和取绝对值
- 需要开 long long
D
假设点 u 的每一个儿子 t 对应的子树都已经平衡了
- 对于权值 <=r[u] 的子树, 显然 u 这个点可以取权值的最大值, 然后每棵子树都整体增加到这一权值即可
- 但有的儿子 t 对应的子树权值 >r[u], 我们就想让 t 对应的子树权值整体减少到 r[u], 才能符合上一条
- 这等效于让 t 对应的子树以外的点权值整体增加, 根据题目所需要的操作为: 选择 t 为根, 对 u 这棵子树操作
- 而这样操作等效于对整棵树最后的答案增加了, 我们额外记录这一部分即可, 而在代码中, 我们呈现为把子树权值减小到 r[u] 了
int cnt = 0; auto dfs = [&] (auto self, int u, int fa) -> int { int mx = l[u]; for (auto t : g[u]) { if (t == fa) { continue; } int temp = self(self, t, u); if (temp > r[u]) { cnt += temp - r[u]; temp = r[u]; } mx = max(temp, mx); } return mx; }; cout << dfs(dfs, 1, -1) + cnt << "\n";需要开 long long
E1
如果一个点, 比这个点权值大的点全在这个点的子树内, 显然不能选这个点
按权值从大到小, 找到第一个不符合上述条件的点即为答案
检查"比这个点权值大的点全在这个点的子树内"是否满足, 可以用线段树 + dfs 序
int cnt = 0; // 记录比当前权值大的点的总数 for (int i = n; i >= 1; i--) { // 遍历权值 for (auto w : v[i]) { // 查询子树中比当前权值大的点的数量 int x = query(1, n, dfn[w], bot[w], 1); if (x != cnt) { cout << w << "\n"; return; } } for (auto w : v[i]) { update(1, n, dfn[w], 1); } cnt += v[i].size(); }完整代码
const int N = 4e5 + 5;
struct segt {
int sum;
}tr[N << 2];
#define ls i<<1
#define rs i<<1|1
inline void pushup(int i) {
tr[i].sum = tr[ls].sum + tr[rs].sum;
}
inline void build(int l, int r, int i) {
tr[i].sum = 0;
if (l == r) {
return;
}
int mid = l + r >> 1;
build(l, mid, ls), build(mid + 1, r, rs);
}
inline void update(int l, int r, int x, int i) {
if (l == r) {
tr[i].sum++;
return;
}
int mid = l + r >> 1;
if (x <= mid) update(l, mid, x, ls);
else update(mid + 1, r, x, rs);
pushup(i);
}
inline int query(int l, int r, int s, int t, int i) {
if (s <= l && r <= t) return tr[i].sum;
int mid = l + r >> 1;
if (t <= mid) return query(l, mid, s, t, ls);
if (s > mid) return query(mid + 1, r, s, t, rs);
return query(l, mid, s, t, ls) + query(mid + 1, r, s, t, rs);
}
int n;
inline void solve() {
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
vector<vector<int> > g(n + 1);
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
int tim = 0;
vector<int> dfn(n + 1), bot(n + 1);
auto dfs = [&] (auto self, int u, int fa) -> void {
dfn[u] = ++tim;
for (auto t : g[u]) {
if (t == fa) {
continue;
}
self(self, t, u);
}
bot[u] = tim;
};
dfs(dfs, 1, -1);
vector<vector<int> > v(n + 1);
for (int i = 1; i <= n; i++) {
v[a[i]].push_back(i);
}
build(1, n, 1);
int cnt = 0;
for (int i = n; i >= 1; i--) {
for (auto w : v[i]) {
int x = query(1, n, dfn[w], bot[w], 1);
if (x != cnt) {
cout << w << "\n";
return;
}
}
for (auto w : v[i]) {
update(1, n, dfn[w], 1);
}
cnt += v[i].size();
}
cout << "0\n";
}
E2
记点 u 的 dfs 序为 dfn[u], 点 u 子树中最大的 dfs 序为 bot[u], [dfn[u],bot[u]] 这个 dfs 序区间就表示 u 这棵子树
仍然是同样于 E1 的思路, Cirno 的策略是: 第一轮操作后, 第二轮 Daiyousei 可以操作, 而第三轮 Cirno 就无法操作而胜利了
那么有两点要满足:
- 第一轮 Cirno 选的点 u 要满足 u 的子树外有比 u 大的点 v
- 而对于上述所有 v, 要满足 v 的子树外比 v 大的点 t, 要么不存在, 要么在 u 的子树内
我们使用两个线段树来维护 dfs 序区间的 lca(因为 lca 具有结合律), 根据值从大到小遍历节点, 并更新线段树, 这样遍历到某个点 u 时线段树中只有比 u 大的点的贡献
第一个线段树中, 在 dfn[u] 处的值就是 u 自身
- 查询 [1,dfn[u]-1] 的 lca 和 [bot[u]+1,n] 的 lca, 这两者的 lca 记为 x
- x 即为所有 v 的 lca
- 如果 x 不存在, 说明 v 不存在, 这个点不可行
int x = pushup(lcaTr1.query(1, n, 1, dfn[u] - 1, 1), lcaTr1.query(1, n, bot[u] + 1, n, 1)); if (x == -1) { continue; }第二个线段树中, 在 dfn[u] 处的值是 u 子树外所有比 u 大的点 v 的 lca, 这个 lca 就是第一个线段树中得到的 x
- 查询 [1,dfn[u]-1] 的 lca 和 [bot[u]+1,n] 的 lca, 这两者的 lca 记为 y
- y 即为所有 t 的 lca
- 如果 y 不存在, 或者 y 在 u 的子树内, 说明所有 t 不存在或者在 u 的子树中, 这个点可行
int y = pushup(lcaTr2.query(1, n, 1, dfn[u] - 1, 1), lcaTr2.query(1, n, bot[u] + 1, n, 1)); if (y == -1 || (dfn[u] <= dfn[y] && dfn[y] <= bot[u])) { ans.push_back(u); }
完整代码
const int N = 4e5 + 5;
int dfn[N], bot[N];
int dep[N], p[N][19];
inline int lca(int a, int b) {
if (dep[a] > dep[b]) swap(a,b);
int temp = dep[b] - dep[a];
for (int i = 0; temp; i++, temp >>= 1) {
if (temp & 1) b = p[b][i];
}
if (a == b) return a;
for (int i = 18; i >= 0; i--) {
if (p[a][i] != p[b][i]) {
a = p[a][i];
b = p[b][i];
}
}
return p[a][0];
}
inline int pushup(int a, int b) {
int c = -1;
if (a == -1 && b == -1) {
c = -1;
} else if (a == -1) {
c = b;
} else if (b == -1) {
c = a;
} else {
c = lca(a, b);
}
return c;
}
struct segt{
int tr[N << 2];
#define ls i << 1
#define rs i << 1 | 1
inline void build(int l, int r, int i) {
tr[i] = -1;
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, ls), build(mid + 1, r, rs);
}
inline void update(int l, int r, int x, int w, int i) {
if (l == r) {
tr[i] = w;
return;
}
int mid = l + r >> 1;
if (x <= mid) update(l, mid, x, w, ls);
else update(mid + 1, r, x, w, rs);
tr[i] = pushup(tr[ls], tr[rs]);
}
inline int query(int l, int r, int s, int t, int i) {
if (s > t) return -1;
if (s <= l && r <= t) return tr[i];
int mid = l + r >> 1;
if (t <= mid) return query(l, mid, s, t, ls);
if (s > mid) return query(mid + 1, r, s, t, rs);
return pushup(query(l, mid, s, t, ls), query(mid + 1, r, s, t, rs));
}
}lcaTr1, lcaTr2;
int n;
inline void solve() {
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
vector<vector<int> > g(n + 1);
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
int tim = 0;
auto dfs = [&] (auto self, int u, int fa) -> void {
dfn[u] = ++tim;
for (auto t : g[u]) {
if (t == fa) {
continue;
}
self(self, t, u);
}
bot[u] = tim;
};
dfs(dfs, 1, -1);
for (int i = 1; i <= n; i++) {
for (int t = 18; t >= 0; t--) p[i][t] = 0;
}
auto dfs2 = [&] (auto self, int u, int fa) -> void {
dep[u] = dep[fa] + 1, p[u][0] = fa;
for (int i = 1; (1 << i) <= dep[u]; i++) {
p[u][i] = p[p[u][i - 1]][i - 1];
}
for (auto t : g[u]) {
if (t == fa) {
continue;
}
self(self, t, u);
}
};
dfs2(dfs2, 1, 0);
vector<vector<int> > v(n + 1);
for (int i = 1; i <= n; i++) {
v[a[i]].push_back(i);
}
lcaTr1.build(1, n, 1), lcaTr2.build(1, n, 1);
vector<int> ans;
for (int i = n; i >= 1; i--) {
for (auto u : v[i]) {
int x = pushup(lcaTr1.query(1, n, 1, dfn[u] - 1, 1), lcaTr1.query(1, n, bot[u] + 1, n, 1));
if (x == -1) {
continue;
}
int y = pushup(lcaTr2.query(1, n, 1, dfn[u] - 1, 1), lcaTr2.query(1, n, bot[u] + 1, n, 1));
if (y == -1 || (dfn[u] <= dfn[y] && dfn[y] <= bot[u])) {
ans.push_back(u);
}
}
for (auto u : v[i]) {
int x = pushup(lcaTr1.query(1, n, 1, dfn[u] - 1, 1), lcaTr1.query(1, n, bot[u] + 1, n, 1));
if (x != -1) {
lcaTr2.update(1, n, dfn[u], x, 1);
}
}
for (auto u : v[i]) {
lcaTr1.update(1, n, dfn[u], u, 1);
}
}
sort(ans.begin(), ans.end());
cout << ans.size() << " ";
for (auto w : ans) cout << w << " ";
cout << "\n";
}