特辑:线段树

太久不打线段树手已生

Problem A: 高速公路

假期望题,分母显然为 \(C_{R - L + 1}^2\).把区间内所有子段的和求出来就万事了。

然后考虑线段树区间合并,似乎不可做。

这时SX红太阳郭老师开导了我,我们可以采取类似于树上染色的思路,考虑每条边可以被经过几次。

这就比较显然了,为\((R - i + 1)(i - L + 1)\).那么分子就是\(\sum \limits _{i = L} ^ R (R - i + 1)(i - L + 1) w_i\).

化简一下就是 \(\sum \limits _{i = L} ^ R (R - L + 1 - LR)w_i + (L + R) i w_i - i^2 w_i\).

线段树记3个tag:\(s_1 = w_i, s_2 = i w_i, s_3 = i^2 w_i\).

区间合并直接加起来。

再考虑修改边权,就是加个 \(v (R - L + 1 - LR)\) \(v (L + R) \sum i\) 和 $ -v i ^2$.

再记\(s_4 = \sum i, s_5 = \sum i^2\).

这道题就完了。

注意坑点:

  1. 给的是点,我们维护的是线段,需要处理一下
  2. 能开 long long 就开 long long !!别吝啬!! 如果你WA20了,大约就是该开的long long没开
#include <bits/stdc++.h>
#define ll long long

const int N = 100000 + 233;
ll n, m, sum[5]; char opt[5];
struct SegTree { ll l, r, s[8], tag; } t[N << 2];

inline void Pushup(ll p) {
    for (int i = 1; i <= 3; i++)
        t[p].s[i] = t[p << 1].s[i] + t[p << 1 | 1].s[i];
}

ll gcd(ll x, ll y) {
    return y == 0 ? x : gcd(y, x % y);
}

void Build(ll p, ll l, ll r) {
    t[p].l = l, t[p].r = r;
    if (l == r) return (void) (t[p].s[4] = l, t[p].s[5] = l * l);
    ll mid = (l + r) >> 1;
    Build(p << 1, l, mid), Build(p << 1 | 1, mid + 1, r);
    for (int i = 4; i <= 5; i++)
        t[p].s[i] = t[p << 1].s[i] + t[p << 1 | 1].s[i];
}

inline void Add(ll p, ll k) {
    t[p].s[1] += (t[p].r - t[p].l + 1) * k;
    t[p].s[2] += k * t[p].s[4];
    t[p].s[3] += k * t[p].s[5];
    t[p].tag += k;
}

inline void Pushdown(ll p) {
    Add(p << 1, t[p].tag), Add(p << 1 | 1, t[p].tag);
    t[p].tag = 0;
}

void Change(ll p, ll l, ll r, ll v) {
    if (l <= t[p].l && r >= t[p].r) return (void) Add(p, v);
    ll mid = (t[p].l + t[p].r) >> 1;
    if (t[p].tag) Pushdown(p);
    if (l <= mid) Change(p << 1, l, r, v);
    if (r > mid) Change(p << 1 | 1, l, r, v);
    Pushup(p);
}

void Ask(ll p, ll l, ll r) {
    if (l <= t[p].l && r >= t[p].r) {
        for (int i = 1; i <= 3; i++)
            sum[i] += t[p].s[i];
        return;
    }
    ll mid = (t[p].l + t[p].r) >> 1;
    if (t[p].tag) Pushdown(p);
    if (l <= mid) Ask(p << 1, l, r);
    if (r > mid) Ask(p << 1 | 1, l, r);
}

inline void print(ll x, ll y) {
    ll g = gcd(x, y);
    printf("%lld/%lld\n", x / g, y / g);
}

signed main() {
    scanf("%lld%lld", &n, &m);
    Build(1, 1, n);
    for (ll i = 1, l, r; i <= m; i++) {
        ll v;
        scanf("%s%lld%lld", opt, &l, &r); r--;
        if (opt[0] == 'C') {
            scanf("%lld", &v), Change(1, l, r, v);
        } else {
            sum[1] = sum[2] = sum[3] = 0;
            Ask(1, l, r);
            ll a = (r - l + 1 - l * r) * sum[1] + (r + l) * sum[2] - sum[3];
            ll b = (r - l + 2) * (r - l + 1) / 2;
            print(a, b);
        }
    }
    return 0;
}

Problem D:排序

第四次听这题(

第一次在SJZEZ听死宅邢泽宇讲

第二次在SDFZ听高胜寒鸽鸽讲

第三次在TYWZ听真-SX红太阳李嘉图聚聚讲

第四次在HZ听考拉巨巨讲

真实大众题(

本题最重要的一点就是询问只有一个位置,而答案显然具有单调性,你二分就完事

给排列排序太磨叽,转成01序列,0表示小于,1为大于等于,这样就可以用线段树维护“排序”的过程。

然而本题数据过水,桶排神优化一下也能过(((

#include <bits/stdc++.h>

const int N = 100005 + 233;
int n, m, q, ans, a[N];
struct Command { int l, r, op; } cmd[N];
struct SegTree {
    int l, r, sum, tag;
    #define l(p) tree[p].l
    #define r(p) tree[p].r
    #define sum(p) tree[p].sum
    #define tag(p) tree[p].tag
    #define ls(p) p << 1
    #define rs(p) p << 1 | 1
} tree[N << 2];

inline int R() {
    int a = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    return a;
}

void Build(int p, int l, int r, int v) {
    l(p) = l, r(p) = r, tag(p) = -1;
    if (l == r) return (void) (sum(p) = a[l] >= v);
    int mid = (l + r) >> 1;
    Build(ls(p), l, mid, v), Build(rs(p), mid + 1, r, v);
    sum(p) = sum(ls(p)) + sum(rs(p));
}

void Pushdown(int p) {
    if (tag(p) != -1) {
        sum(ls(p)) = (r(ls(p)) - l(ls(p)) + 1) * tag(p);
        sum(rs(p)) = (r(rs(p)) - l(rs(p)) + 1) * tag(p);
        tag(ls(p)) = tag(rs(p)) = tag(p);
        tag(p) = -1;
    }
}

void Change(int p, int l, int r, int v) {
    if (l <= l(p) && r >= r(p)) {
        sum(p) = (r(p) - l(p) + 1) * v;
        tag(p) = v;
    } else {
        Pushdown(p);
        int mid = (l(p) + r(p)) >> 1;
        if (l <= mid) Change(ls(p), l, r, v);
        if (r > mid) Change(rs(p), l, r, v);
        sum(p) = sum(ls(p)) + sum(rs(p));
    }
}

int Query(int p, int l, int r) {
    if (l <= l(p) && r >= r(p)) 
        return sum(p);
    Pushdown(p);
    int mid = (l(p) + r(p)) >> 1, ret = 0;
    if (l <= mid) ret += Query(ls(p), l, r);
    if (r > mid) ret += Query(rs(p), l, r);
    return ret;
}

bool Check(int x) {
    Build(1, 1, n, x);
    for (int i = 1; i <= m; i++) {
        int cnt = Query(1, cmd[i].l, cmd[i].r);
        if (cnt != 0 && cnt != cmd[i].r - cmd[i].l + 1) {
            if (cmd[i].op) {
                Change(1, cmd[i].l, cmd[i].l + cnt - 1, 1);
                Change(1, cmd[i].l + cnt, cmd[i].r, 0);
            } else {
                Change(1, cmd[i].l, cmd[i].r - cnt, 0);
                Change(1, cmd[i].r - cnt + 1, cmd[i].r, 1);
            }
        }
    }
    return Query(1, q, q);
}

signed main() {
    n = R(), m = R();
    for (int i = 1; i <= n; i++)
        a[i] = R();
    for (int i = 1; i <= m; i++)
        cmd[i].op = R(), cmd[i].l = R(), cmd[i].r = R();
    q = R();
    int l = 1, r = n, mid;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (Check(mid)) {
            ans = mid;
            l = mid + 1;
        } else r = mid - 1;
    }
    return !printf("%d\n", ans);
}

猜你喜欢

转载自www.cnblogs.com/gekoo/p/11240280.html