Codeforces gym103119J 线段树

题意

传送门 Codeforces gym103119J Jewel Grab

题解

维护位置 i i i 相同颜色的前一个位置 p r e [ i ] pre[i] pre[i],每次查询从当前起点 l l l 出发,可走到的区间 [ l , r ) [l,r) [l,r) 满足 p r e [ i ] < s , i ∈ [ l , r ) pre[i]<s,i\in [l,r) pre[i]<s,i[l,r);此时 r r r 对应的颜色出现了多次(可走到这样的 r r r 至多 k + 1 k+1 k+1 个),需要维护价值的最大值,然后再从 r + 1 r+1 r+1 出发拓展。

对于满足上述条件的 r r r 可以用线段树加速,具体而言,先在树上找到满足大于等于 l l l 的位置,然后从左向右不断拓展。

总时间复杂度 O ( m k log ⁡ n ) O(mk\log n) O(mklogn)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int MAXN = 2E5 + 5, SZ = 1 << 19;
int N, M, C[MAXN], V[MAXN];
int lst[MAXN], pre[MAXN];
set<int> pos[MAXN];

struct SG
{
    int mx[SZ];
    ll sum[SZ];
    void init(int k = 0, int l = 0, int r = N)
    {
        if (r - l == 1)
        {
            mx[k] = pre[l], sum[k] = V[l];
            return;
        }
        int m = (l + r) / 2, chl = k * 2 + 1, chr = k * 2 + 2;
        init(chl, l, m), init(chr, m, r);
        mx[k] = max(mx[chl], mx[chr]);
        sum[k] = sum[chl] + sum[chr];
    }
    void change(int a, int b, int k = 0, int l = 0, int r = N)
    {
        if (r <= a || b <= l)
            return;
        if (a <= l && r <= b)
        {
            mx[k] = pre[l], sum[k] = V[l];
            return;
        }
        int m = (l + r) / 2, chl = k * 2 + 1, chr = k * 2 + 2;
        change(a, b, chl, l, m), change(a, b, chr, m, r);
        mx[k] = max(mx[chl], mx[chr]);
        sum[k] = sum[chl] + sum[chr];
    }
    ll ask_sum(int a, int b, int k = 0, int l = 0, int r = N)
    {
        if (r <= a || b <= l)
            return 0;
        if (a <= l && r <= b)
            return sum[k];
        int m = (l + r) / 2, chl = k * 2 + 1, chr = k * 2 + 2;
        return ask_sum(a, b, chl, l, m) + ask_sum(a, b, chr, m, r);
    }
    int ask_pos(int a, int b, int x, int k = 0, int l = 0, int r = N)
    {
        if (a <= l && mx[k] < x)
            return r;
        if (r - l == 1)
            return l;
        int m = (l + r) / 2, chl = k * 2 + 1, chr = k * 2 + 2;
        if (m <= a)
            return ask_pos(a, b, x, chr, m, r);
        int j = ask_pos(a, b, x, chl, l, m);
        if (j == m)
            j = ask_pos(a, b, x, chr, m, r);
        return j;
    }
} tr;

void _max(int &x, int y) { x = max(x, y); }

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> N >> M;
    for (int i = 0; i < N; ++i)
        cin >> C[i] >> V[i];
    memset(lst, -1, sizeof(lst));
    for (int i = 0; i < N; ++i)
    {
        pos[C[i]].insert(i);
        pre[i] = lst[C[i]];
        lst[C[i]] = i;
    }
    tr.init();
    while (M--)
    {
        int op;
        cin >> op;
        if (op == 1)
        {
            int x, c, v;
            cin >> x >> c >> v;
            --x;
            auto it = pos[C[x]].lower_bound(x);
            auto it2 = it;
            ++it2;
            if (it2 != pos[C[x]].end())
            {
                int y = *it2;
                if (it == pos[C[x]].begin())
                    pre[y] = -1;
                else
                    pre[y] = *(--it);
                tr.change(y, y + 1);
            }
            pos[C[x]].erase(x);
            C[x] = c, V[x] = v;
            pos[C[x]].insert(x);
            it = pos[C[x]].lower_bound(x);
            it2 = it, ++it2;
            if (it2 != pos[C[x]].end())
            {
                int y = *it2;
                pre[y] = x;
                tr.change(y, y + 1);
            }

            pre[x] = it == pos[C[x]].begin() ? -1 : *(--it);
            tr.change(x, x + 1);
        }
        else
        {
            int s, k;
            cin >> s >> k;
            --s;
            ll sum = 0;
            map<int, int> mp;
            for (int l = s; l < N;)
            {
                int j = tr.ask_pos(l, N, s);
                sum += tr.ask_sum(l, j);
                if (k == 0 || j == N)
                    break;
                int c = C[j], v = V[j];
                if (!mp.count(c))
                {
                    sum -= V[pre[j]];
                    _max(mp[c], V[pre[j]]);
                }
                _max(mp[c], v);
                --k;
                l = j + 1;
            }
            for (auto &p : mp)
                sum += p.second;
            cout << sum << '\n';
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值