「 专题 」线段树

· · 个人记录

\texttt{「CF240F」TorCoder}

\texttt{Solution}

用线段树维护每个区间每种字符的个数。

修改时枚举每种字符区间赋值,这样只会枚举 26 次。

时间复杂度大约是 \mathcal O (26 n \log n)

\texttt{Code}

#include <iostream>

using namespace std;

const int MAX_n = 1e5;
const int MAX_m = 1e5;

int n, m, zh[26];

char str[MAX_n + 5];

struct Tree {
    int num, cur[26];
} tree[MAX_n * 4 + 5];

void spread(int u, int L, int R, int x) {
    if (x != -1) {
        for (int i = 0; i < 26; i++)
            tree[u].cur[i] = 0;
        tree[u].cur[tree[u].num = x] = R - L + 1;
    }
}

void pushup(int u) {
    for (int i = 0; i < 26; i++)
        tree[u].cur[i] = tree[u << 1].cur[i] + tree[u << 1 | 1].cur[i];
}

void pushdown(int u, int L, int R) {
    int Mid = (L + R) >> 1;
    spread(u << 1, L, Mid, tree[u].num);
    spread(u << 1 | 1, Mid + 1, R, tree[u].num);
    tree[u].num = -1;
}

void build(int u, int L, int R) {
    tree[u].num = -1; if (L == R) { tree[u].cur[tree[u].num = str[L] - 'a'] = 1; return ; }
    int Mid = (L + R) >> 1; build(u << 1, L, Mid); build(u << 1 | 1, Mid + 1, R); pushup(u);
}

void upd(int u, int L, int R, int qL, int qR, int x) {
    if (qL <= L && R <= qR) { spread(u, L, R, x); return ; }
    int Mid = (L + R) >> 1; pushdown(u, L, R);
    if (qL <= Mid) upd(u << 1, L, Mid, qL, qR, x);
    if (qR >= Mid + 1) upd(u << 1 | 1, Mid + 1, R, qL, qR, x);
    pushup(u);
}

int Ask(int u, int L, int R, int qL, int qR) {
    if (qL <= L && R <= qR) {
        for (int i = 0; i < 26; i++)
            zh[i] += tree[u].cur[i];
        return tree[u].num;
    }
    int res = 0, Mid = (L + R) >> 1; pushdown(u, L, R);
    if (qL <= Mid) res += Ask(u << 1, L, Mid, qL, qR);
    if (qR >= Mid + 1) res += Ask(u << 1 | 1, Mid + 1, R, qL, qR);
    return res;
}

int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin >> n >> m >> (str + 1);
    int L, R; build(1, 1, n);
    while (m--) {
        cin >> L >> R;
        for (int i = 0; i < 26; i++) zh[i] = 0;
        Ask(1, 1, n, L, R);
        int now = -1;
        for (int i = 0; i < 26 && now != -2; i++) {
            if (zh[i] & 1) {
                if (now == -1) now = i;
                else if (now > -1) now = -2;
            }
        }
        if (now == -2) continue;
        else if (now != -1) upd(1, 1, n, (L + R) >> 1, (L + R) >> 1, now), --zh[now];
        for (int i = 0; i < 26; i++) {
            if (zh[i]) {
                zh[i] >>= 1;
                upd(1, 1, n, L, L + zh[i] - 1, i);
                upd(1, 1, n, R - zh[i] + 1, R, i);
                L = L + zh[i];
                R = R - zh[i];
            }
        }
    }
    for (int i = 1; i <= n; i++)
        cout << (char)(Ask(1, 1, n, i, i) + 'a');
    cout << endl;
    return 0;
}

\texttt{「CF1439C」Greedy Shopping}

\texttt{Soluton}

因为修改操作修改的是前缀,所以 a 始终为 不上升 序列。

用线段树二分修改和回答询问即可。

时间复杂度 \mathcal O (n \log n)

\texttt{Code}

#include <iostream>

using namespace std;

#define LL long long
#define uLL unsigned LL

const int MAX_n = 2e5;
const int MAX_m = 2e5;

int n, m;
int b[MAX_n + 5];

struct Tree {
    LL num; int numL, numR, tag;
} tree[MAX_n * 4 + 5];

void spread(int u, int L, int R, int x) {
    if (x) {
        tree[u].num = (LL)x * (R - L + 1);
        tree[u].numL = tree[u].numR = tree[u].tag = x;
    }
}

void pushup(int u) {
    tree[u].num = tree[u << 1].num + tree[u << 1 | 1].num;
    tree[u].numL = tree[u << 1].numL;
    tree[u].numR = tree[u << 1 | 1].numR;
}

void pushdown(int u, int L, int R) {
    int Mid = (L + R) >> 1;
    spread(u << 1, L, Mid, tree[u].tag);
    spread(u << 1 | 1, Mid + 1, R, tree[u].tag);
    tree[u].tag = 0;
}

void build(int u, int L, int R) {
    if (L == R) { tree[u].numL = tree[u].numR = tree[u].num = b[L]; return ; }
    int Mid = (L + R) >> 1; build(u << 1, L, Mid); build(u << 1 | 1, Mid + 1, R); pushup(u);
}

void upd1(int u, int L, int R, int qL, int qR, int y) {
    if (qL <= L && R <= qR) { spread(u, L, R, y); return ; }
    int Mid = (L + R) >> 1; pushdown(u, L, R);
    if (qL <= Mid) upd1(u << 1, L, Mid, qL, qR, y);
    if (qR >= Mid + 1) upd1(u << 1 | 1, Mid + 1, R, qL, qR, y);
    pushup(u);
}

void upd2(int u, int L, int R, int x, int y) {
    if (L == R) {
        if (tree[u].num < y)
            spread(u, L, R, y);
        return ;
    } else {
        int Mid = (L + R) >> 1; pushdown(u, L, R);
        if (x <= Mid) {
            upd2(u << 1, L, Mid, x, y);
        } else {
            if (tree[u << 1].numR >= y) {
                upd2(u << 1 | 1, Mid + 1, R, x, y);
            } else {
                upd1(u << 1 | 1, Mid + 1, R, 1, x, y);
                upd2(u << 1, L, Mid, x, y);
            }
        }
        pushup(u);
    }
}

int Ask(int u, int L, int R, int x, int &y) {
    if (L == R) {
        if (tree[u].num <= y) {
            y -= tree[u].num;
            return 1;
        }
        return 0;
    } else {
        int res = 0, Mid = (L + R) >> 1; pushdown(u, L, R);
        if (x >= Mid + 1) {
            res += Ask(u << 1 | 1, Mid + 1, R, x, y);
        } else {
            res += Ask(u << 1, L, Mid, x, y);
            if (tree[u << 1 | 1].num <= y) {
                res += (R - Mid);
                y -= tree[u << 1 | 1].num;
            } else if (tree[u << 1 | 1].numR <= y) {
                res += Ask(u << 1 | 1, Mid + 1, R, x, y);
            }
        }
        return res;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> b[i];
    int type, x, y; build(1, 1, n);
    while (m--) {
        cin >> type >> x >> y;
        switch (type) {
            case 1 : { upd2(1, 1, n, x, y); break; }
            case 2 : { cout << Ask(1, 1, n, x, y) << endl; break; }
        }
    }
    return 0;
}

\texttt{「CF1614E」Divan and a Cottage}

\texttt{Soluton}

权值线段树维护每一种初始温度第 i 的结果,可以发现这个结果始终是 不下降 的,因此线段树二分即可。

时间复杂度 \mathcal O (n \log 10^9)

\texttt{Code}

#include <iostream>

using namespace std;

const int mod = 1e9 + 1;
const int mx = 6e6;

int n, m, tr, node, lastans;

struct Tree {
    int ls, rs, num, numL, numR;
} tree[mx + 5];

void pushup(int u) {
    tree[u].numL = tree[tree[u].ls].numL + tree[u].num;
    tree[u].numR = tree[tree[u].rs].numR + tree[u].num;
}

void spread(int &u, int x) {
    if (!u) u = ++node;
    tree[u].num += x;
    tree[u].numL += x;
    tree[u].numR += x;
}

void upd(int &u, int L, int R, int qL, int qR, int x) {
    if (qL > qR) return ;
    else if (!u) u = ++node;
    if (qL <= L && R <= qR) { spread(u, x); return ; }
    int Mid = (L + R) >> 1;
    if (qL <= Mid) upd(tree[u].ls, L, Mid, qL, qR, x);
    if (qR >= Mid + 1) upd(tree[u].rs, Mid + 1, R, qL, qR, x);
    pushup(u);
}

int Ask(int u, int L, int R, int x) {
    if (!u) return 0;
    else if (L == R) return tree[u].num;
    int res = tree[u].num, Mid = (L + R) >> 1;
    if (x <= Mid) res += Ask(tree[u].ls, L, Mid, x);
    else res += Ask(tree[u].rs, Mid + 1, R, x);
    return res;
}

void upd1(int &u, int L, int R, int T, int now) {
    if (!u) u = ++node;
    int Mid = (L + R) >> 1;
    if (L == R) {
        if (L + tree[u].num + now < T)
            spread(u, 1);
        return ;
    } else {
        now += tree[u].num;
        if (Mid + tree[tree[u].ls].numR + now < T) {
            spread(tree[u].ls, 1);
            upd1(tree[u].rs, Mid + 1, R, T, now);
        } else {
            upd1(tree[u].ls, L, Mid, T, now);
        }
        pushup(u);
    }
}

void upd2(int &u, int L, int R, int T, int now) {
    if (!u) u = ++node;
    int Mid = (L + R) >> 1;
    if (L == R) {
        if (L + tree[u].num + now > T)
            spread(u, -1);
        return ;
    } else {
        now += tree[u].num;
        if (Mid + 1 + tree[tree[u].rs].numL + now > T) {
            spread(tree[u].rs, -1);
            upd2(tree[u].ls, L, Mid, T, now);
        } else {
            upd2(tree[u].rs, Mid + 1, R, T, now);
        }
        pushup(u);
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1, T, k, x, L, R; i <= n; i++) {
        cin >> T;
        upd1(tr, 0, 1e9, T, 0);
        upd2(tr, 0, 1e9, T, 0);
        cin >> k;
        while (k--) {
            cin >> x; x = (x + lastans) % mod;
            cout << (lastans = (x + Ask(tr, 0, 1e9, x))) << endl;
        }
    }
    return 0;
}