[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\),而且不用写主席树!

posted @ 2025-07-19 10:35  MoyouSayuki  阅读(154)  评论(0)    收藏  举报
:name :name