P6619 冰火战士

咱就是说,一道树桩数组好题。
作前缀和可以得到冰战士的能量贡献,后缀和得到火战士的能量贡献。 每时每刻冰战士能贡献的最大能量是单调递增的,而火战士是单调递减的。
作 $f(x)$ 为冰战士的能量贡献, $g(x)$ 为火战士的能量贡献,$Q$ 为温度集合。得到:$$ans \gets \max_{i \in Q} \min \left \{ f(i), g(i) \right \} $$
作图可知答案函数为单峰函数,看似可以用三分,但是它并不严格单调,即存在某个区间,其函数值都相同,所以没办法三分。换个角度,我们可以在 $f(x)$, $g(x)$ 上二分找出最大的满足 $f(x) \le g(x)$ 的 $k$,然后再在 $g(x)$ 上找到最大的 $k^{'}$ 满足 $g(k + 1) = g(k^{'})$,比较俩点答案,输出即可。

考虑实现,如果我们使用数据结构维护前/后缀和,再套二分。那么这样时间复杂度是 $O(n\log^2 n)$ 的,铁定过不去。但是树状树组有一个很神奇的性质:$p[i + 2^p]$ 维护了 $ \left [ i,i+2^p \right ] $ 的所有信息。据此我们可以直接在树状树组上倍增,单次时间复杂度降至 $O(\log n)$。

实际实现中我们可以在火战士树状树组的第一位存下所有贡献,在相应位置加上负能量值,就可以用前缀实现后缀了。

小清新的代码实现:

#include<bits/stdc++.h>
using namespace std;
#define lb(x) (x & (-x)) 
#define rp(i, a, b) for(int i = a;i <= b;i++) 
const int N = 2e6 + 50;
struct BIT {
    int c[N << 2], len;
    void add(int x, int v) {
        for(; x <= len ; x += lb(x)) c[x] += v;
    }
    int query(int x) {
        int ans = 0;
        for(; x ; x -= lb(x)) ans += c[x];
        return ans;
    }
}tf, ti;
struct node { int opt, t, x, y, k; };
int Q, tot;
int lg[N], lsh[N];
node oper[N];
int half_search() {
    int p = 0;
    int sumi = 0, sumf = 0;
    for(int t = 1 << lg[tot] ; t >= 1 ; t >>= 1) { 
        if(p + t <= tot && sumi + ti.c[p +t] <= sumf + tf.c[p + t]){ 
            p += t;
            sumi += ti.c[p];
            sumf += tf.c[p];
        }     
    }
    return p;
}
int self_search(int p) {
    if(p > tot) return 0;
    int sumt, sumf = tf.query(p);
    sumt = p = 0;
    for(int t = 1 << lg[tot]; t >= 1 ; t >>= 1) {
        if(p + t <= tot && sumt + tf.c[p + t] >= sumf) {
            p += t;
            sumt += tf.c[p];
        }
    }
    return p; 
}
int main() {
    scanf("%d", &Q);
    rp(i, 1, Q) {
        scanf("%d", &oper[i].opt);
        if(oper[i].opt == 1) scanf("%d %d %d", &oper[i].t, &oper[i].x, &oper[i].y), lsh[++tot] = oper[i].x;
        else scanf("%d", &oper[i].k);
    }
    sort(lsh + 1, lsh + tot + 1);
    tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
    rp(i, 1, Q) oper[i].x = lower_bound(lsh + 1, lsh + tot + 1, oper[i].x) - lsh;

    lg[0] = -1;
    rp(i, 1, tot) lg[i] = lg[i >> 1] + 1; 

    tf.len = ti.len = tot;

    rp(i, 1, Q) {
        int id = (oper[i].opt == 1 ? i : oper[i].k);
        int tc = (oper[i].opt == 1 ? 1 : -1);
        if(oper[id].t == 0) 
            ti.add(oper[id].x, oper[id].y * tc); 
        else 
            tf.add(1, oper[id].y * tc),
            tf.add(oper[id].x + 1, -oper[id].y* tc);
        int rk = half_search();
        int rkk = self_search(rk + 1);
        int ans1 = ti.query(rk) * 2;
        int ans2 = tf.query(rkk) * 2; 
        if(max(ans1, ans2) == 0) printf("Peace\n");
        else if(ans1 > ans2) printf("%d %d\n", lsh[rk], ans1);
        else printf("%d %d\n", lsh[rkk], ans2);
    }
    return 0;
}
posted @ 2023-09-22 09:21  Saka_Noa  阅读(16)  评论(0)    收藏  举报  来源