[CF2126] Codeforces Round 1037 (Div. 3) 题解
[CF2126] Codeforces Round 1037 (Div. 3) 题解
A. Only One Digit
找到最小的数位即可。
B. No Casino in the Mountains
贪心模拟即可。
C. I Will Definitely Make It
可以发现,直接走到一个高山和经过一个过渡的中高山的时间是一样的,所以按山的高度排序,模拟依次爬山即可。
D. This Is the Last Time
容易证明,任意时刻拥有的钱一定是不降的,所以我们把 \(r_i\leftarrow real_i\),然后按 \(l_i\) 排序,每次遇到一个赌场,如果 \(x \ge l_i\),\(x = \max(x, r_i)\)。
E. G-C-D, Unlucky!
感觉我的做法很麻烦,考虑质因数分解每个数,如果某个前缀的一个质数幂 \(\alpha\) 比上一个前缀的这个指数幂 \(\beta\) 小,那么可以发现这个位置上的数字对应的指数幂一定是 \(\beta\) ,否则就等于约束了当前数字的指数幂要不小于 \(\beta\)。
统计所有约束之后检验是否冲突即可。
F. 1-1-1, Free Tree!
典题,用 map 维护每个点儿子每一种颜色对应的边权和,一次修改只对父亲的 map 有影响,可以利用这个 map 快速统计修改这个点之后,这个点和儿子的贡献。
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <map>
#define int long long
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 2e5 + 10;
int n, q, a[N], fa[N], faw[N], ans;
vector<PII> g[N];
map<int, int> cnt[N];
void dfs(int u, int fat) {
fa[u] = fat;
for(auto [v, w] : g[u])
if(v != fat) faw[v] = w, dfs(v, u), cnt[u][a[v]] += w;
ans -= cnt[u][a[u]];
}
void work() {
cin >> n >> q;
for(int i = 1; i <= n; i ++) cin >> a[i], g[i].clear(), cnt[i].clear();
int sum = 0;
ans = 0;
for(int i = 1, a, b, c; i < n; i ++) {
cin >> a >> b >> c, g[a].push_back({b, c}), g[b].push_back({a, c});
sum += c;
}
dfs(1, 0);
for(int i = 1, x, v; i <= q; i ++) {
cin >> x >> v;
if(x > 1) cnt[fa[x]][a[x]] -= faw[x];
if(x > 1 && a[x] == a[fa[x]]) ans += faw[x];
ans += cnt[x][a[x]];
a[x] = v;
ans -= cnt[x][a[x]];
if(x > 1 && a[x] == a[fa[x]]) ans -= faw[x];
if(x > 1) cnt[fa[x]][a[x]] += faw[x];
cout << sum + ans << '\n';
}
return ;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) work();
return 0;
}
G2. Big Wins! (hard version)
直接冲 G2 没调出来遗憾离场,这题我可能也做复杂了。
用笛卡尔树(最小值分治)处理 \(\min\) 的限制,然后二分中位数 \(mid\),引入一个数组 \(b_i\):
\[b_i =\left\{\begin{matrix}
1& a_i \ge mid \\
-1& a_i < mid
\end{matrix}\right.
\]
可以发现,如果一个 \(b_i\) 区间的和非负,则这个区间的中位数 \(\ge mid\),用一棵主席树维护区间加,区间 \(\max\) 即可,标记永久化或者每次下传标记的时候创建新节点。
时间复杂度 \(O(n\log^2 n)\)。
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 2e5 + 10, M = N * 30, INF = 1e9;
int n, a[N];
int ls[M], rs[M], idx, root[N];
struct qwq {
int mx, mn, tag;
} tr[M];
#define mid ((l + r) >> 1)
void align(int u, int v) {
tr[u].mx += v, tr[u].mn += v, tr[u].tag += v;
}
void up(int u) {
tr[u].mx = max(tr[ls[u]].mx, tr[rs[u]].mx) + tr[u].tag;
tr[u].mn = min(tr[ls[u]].mn, tr[rs[u]].mn) + tr[u].tag;
}
int build(int l, int r) {
int u = ++ idx;
tr[u] = {0, 0, 0};
if(l == r) {
return tr[u].mx = tr[u].mn = l, u;
}
ls[u] = build(l, mid), rs[u] = build(mid + 1, r), up(u);
return u;
}
int update(int q, int l, int r, int ql, int qr, int v) {
int p = ++ idx;
tr[p] = tr[q], ls[p] = ls[q], rs[p] = rs[q];
if(ql <= l && qr >= r) return align(p, v), p;
if(ql <= mid) ls[p] = update(ls[q], l, mid, ql, qr, v);
if(qr > mid) rs[p] = update(rs[q], mid + 1, r, ql, qr, v);
up(p);
return p;
}
int querymax(int p, int l, int r, int ql, int qr) {
if(ql <= l && qr >= r) return tr[p].mx;
int tmp = -INF;
if(ql <= mid) tmp = querymax(ls[p], l, mid, ql, qr);
if(qr > mid) tmp = max(tmp, querymax(rs[p], mid + 1, r, ql, qr));
return tmp + tr[p].tag;
}
int querymin(int p, int l, int r, int ql, int qr) {
if(ql <= l && qr >= r) return tr[p].mn;
int tmp = INF;
if(ql <= mid) tmp = querymin(ls[p], l, mid, ql, qr);
if(qr > mid) tmp = min(tmp, querymin(rs[p], mid + 1, r, ql, qr));
return tmp + tr[p].tag;
}
#undef mid
vector<int> t[N];
struct Ctree {
int ls[N], rs[N], stk[N], L[N], R[N], top, rt;
void Cartesian() { // build Cartesian Tree
bool flg = 0;
top = 0;
for(int i = 1; i <= n; i ++) {
flg = 0;
while(top && a[stk[top]] > a[i]) top --, flg = 1;
if(top) rs[stk[top]] = i; if(flg) ls[i] = stk[top + 1];
stk[++ top] = i;
}
rt = stk[1];
}
} T;
int ans = 0;
void solve(int u, int l, int r) {
if(l >= r) return ;
solve(T.ls[u], l, u - 1), solve(T.rs[u], u + 1, r);
int mn = a[u];
int L = 1, R = n, mid, pos = -1;
while(L <= R) {
mid = L + R >> 1;
if(querymax(root[mid], 0, n, u, r) - querymin(root[mid], 0, n, l - 1, u - 1) >= 0)
L = mid + 1, pos = mid;
else R = mid - 1;
}
ans = max(ans, pos - mn);
}
void work() {
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i], t[i].clear();
for(int i = 1; i <= n; i ++) t[a[i]].push_back(i);
idx = ans = 0;
root[1] = build(0, n);
for(int i = 2; i <= n; i ++) {
root[i] = root[i - 1];
for(auto c : t[i - 1])
root[i] = update(root[i], 0, n, c, n, -2);
}
T.Cartesian();
solve(T.rt, 1, n);
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--) work();
return 0;
}
UPD: 根据官解的观察,\(mn\) 增大的时候 \(med\) 必须增大,否则不优于之前的答案,可以优化掉一个 \(\log\),而且不用写主席树!

QwQ
浙公网安备 33010602011771号