link
A C D,怎么没过 B?我靠,崩溃了牢弟 qwq
A - Strong Password
B - Make Three Regions
这题。。。我居然用 a[2][j] 然后还真只开了 a[2][N] 的大小,结果 cf test 1 多测只输出一个结果,没见过啊,这直接给我干蒙了啊,又是没有调出来,数组空间开小了这种低级错误也能犯
C - Even Positions
一开始以为很不可做,之前是看到字符串匹配之类的就头疼,
不过最后十分钟又看了一下,发现就是简单贪心,总是选择较近的空位配对,两个方向扫一遍,最后三分钟 A 掉了
D - Maximize the Root
树上贪心,(dp?倒是不至于,就是简单的 dfs 加分讨
一种很显然的贪心是,对于一个子树(根节点非叶节点),总是让它的所有点权趋于平均,但是保证子树的根节点比子树中的其他节点的最小值严格不小,这样在向上回溯时能保证非直接儿子不会非法(即点权减小为负数)
对子树跟节点点权 \(a_u\),直接儿子中点权最小值 \(a_v\) 分类讨论
-
当 \(a_u\geq a_v\),只能直接 \(a_u = a_v\)
-
当 \(a_u = a_v - 1\),这种平均状态是标准的,不用改变
-
当 \(a_u < a_v\),要使之趋于平均,\(a_u = a_u + \lfloor \frac{a_v - a_u}{2}\rfloor\),这样就达到要么 \(a_u = a_v\),要么 \(a_u = a_v - 1\) 的效果
当然,根节点是特殊情况,因为它不需要考虑父节点,所以直接 \(a_u = a_u + a_v\)
复杂度 \(O(n)\)
code
#include <bits/stdc++.h>
#define re register int
#define int long longusing namespace std;
const int N = 2e5 + 10, inf = 1e9 + 10;struct Edge
{int to, next;
}e[N << 1];
int idx, h[N];
int T, n, a[N], in[N];inline void add(int x, int y)
{e[++ idx] = (Edge){y, h[x]};h[x] = idx;
}void dfs(int u, int fa)
{int sum = 0, min_son = inf;for (re i = h[u]; i; i = e[i].next){int v = e[i].to;if (v == fa) continue;dfs(v, u);min_son = min(min_son, a[v]); // 注意是比较更新过后的权值
// sum += a[v];}if (u == 1){
// cout << "check " << min_son << '\n';a[u] += min_son;return; }else if (in[u] > 1){if (a[u] >= min_son) a[u] = min_son;else if (a[u] == min_son - 1) a[u] = a[u];else a[u] += floor((double)((min_son - a[u]) / 2));}else return;
}signed main()
{ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> T;while (T --){memset(h, 0, sizeof(h)); idx = 0;memset(in, 0, sizeof(in));cin >> n;for (re i = 1; i <= n; i ++) cin >> a[i];for (re i = 2; i <= n; i ++){int x; cin >> x;add(x, i), add(i, x);in[x] ++, in[i] ++;}dfs(1, 0);// cout << a[3] << '\n';cout << a[1] << '\n';}return 0;
}
E - Level Up
一开始我以为是个 dp?但是不会推
发现题目已经将一个求解问题转化为判定问题,那考虑往判定方向思考
对于 \(k=x\),发现 \(x\) 越小,对于第 \(i\) 个怪兽而言,它越容易逃跑,因为 \(k\) 越小,玩家期望升级越快,期望等级越比怪物等级高,反之则越不容易逃跑。。。似乎存在二分性
考虑 二分 \(k\)
那我们可以二分一个 \(k\),满足在这个 \(k\) 下,第 \(i\) 个怪物 刚好不逃跑,这样询问时就转化为简单的判定了
考虑一个怪物刚好不逃跑的充要条件就是玩家等级不大于怪物等级,最小化 \(k\)
那么玩家到达第 \(i\) 个位置,至少之前要打过 \(a_i\cdot k\) 个怪兽,即升了 \(a_i\) 级变为 \(a_i + 1>a_i\),这样怪兽就刚好逃跑(似乎刚好逃跑更自然想到,反正也一样二分,反过来就可以了
所以,如果之前打过的怪兽数量 \(query\geq a_i\cdot k\),那么就可以判定怪兽会逃跑
而如何求 \(query\) 呢?注意到我们每次二分出来的值的含义是 刚好使该怪兽不会逃跑的 \(k\),即会打该怪兽
那么就很显然了,每次二分完对 \(k\) 值域加贡献 1,判定时求个前缀即可,考虑 线段树 简单维护
时间复杂度 \(O(n\log^2 n)\)
code
#include <bits/stdc++.h>
#define re register int
#define lp p << 1
#define rp p << 1 | 1using namespace std;
typedef long long LL;
const int N = 2e5 + 10;struct Tree
{int l, r, sum;
}t[N << 2];
int n, q, a[N], not_run[N];inline void build(int p, int l, int r)
{t[p].l = l, t[p].r = r;if (l == r) return;int mid = (l + r) >> 1;build(lp, l, mid);build(rp, mid + 1, r);
}inline void update(int p, int x, int k)
{if (t[p].l == x && t[p].r == x) {t[p].sum += k;return;}int mid = (t[p].l + t[p].r) >> 1;if (x <= mid) update(lp, x, k);if (x > mid) update(rp, x, k);t[p].sum = t[lp].sum + t[rp].sum;
}inline LL query(int p, int l, int r)
{if (l <= t[p].l && t[p].r <= r) return t[p].sum;LL res = 0;int mid = (t[p].l + t[p].r) >> 1;if (l <= mid) res += query(lp, l, r);if (r > mid) res += query(rp, l, r);return res;
}inline bool check(int x, int k)
{return (LL)a[x] * k <= query(1, 1, k);
}int main()
{ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n >> q;build(1, 1, n);for (re i = 1; i <= n; i ++) cin >> a[i];for (re i = 1; i <= n; i ++){int l = 1, r = n;while (l < r){int mid = (l + r) >> 1;if (check(i, mid)) l = mid + 1;else r = mid;}update(1, l, 1);not_run[i] = l;}while (q --){int x, k; cin >> x >> k;cout << (k < not_run[x] ? "No" : "Yes") << '\n';}return 0;
}
F - Chips on a Line
*2700 的 dp?感觉很不可做,溜了溜了 ~