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";
}
posted @   wxgmjfhy  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示