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"; }
分类:
codeforces
标签:
算法竞赛
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话