【WC2015模拟2.5】数组

题目大意:

给你n个元素的颜色。
问元素的颜色互不相同的区间有多少个。
支持单点修改。
1<=n<=10^5

题解:

设last[i]表示i元素的颜色上一次出现的位置。
假设我们确定了区间左端点x,
右端点y要满足条件则 max(last[i])<x,(i in [x..y])

再设一个p数组,p[last[i]]=i

<min(p[i])(i>=x)

满足条件的y就有min(p[i])(i>=x)-x个。

这相当于倒着扫,min值的和。

可以线段树套splay维护。

也可以顺着做递增的单调栈来维护。

带修单调栈之前做过一题。

Code:

#include<set>
#include<cstdio>
#define ll long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 1e5 + 5, M = N * 30;

int n, m, x, y, a[N], la[N];

set<int> s[N];

int find(int a, int i) {
    if(s[a].empty() || (*s[a].begin()) >= i) return 0;
    if(*s[a].rbegin() < i) return *s[a].rbegin();
    return *(-- s[a].lower_bound(i));
}

int p[N], t[N * 4]; ll fx[N * 4];

ll query(int i, int x, int y, int q) {
    if(x == y) return min(p[x], q);
    int m = x + y >> 1;
    return t[i + i + 1] >= q ? (query(i + i, x, m, q) + (ll)(y - m) * q) : (fx[i] + query(i + i + 1, m + 1, y, q));
}
void chan(int i, int x, int y, int l) {
    if(x == y) {t[i] = p[x], fx[i] = p[x]; return;}
    int m = x + y >> 1;
    if(l <= m) chan(i + i, x, m, l); else chan(i + i + 1, m + 1, y, l);
    t[i] = min(t[i + i], t[i + i + 1]);
    fx[i] = query(i + i, x, m, t[i + i + 1]);
}
void GG(int x, int y) {
    la[x] = y; p[y] = x;
    if(y) chan(1, 1, n, y);
}

int main() {
    scanf("%d", &n);
    fo(i, 1, n) p[i] = n + 1, chan(1, 1, n, i);
    fo(i, 1, n) {
        scanf("%d", &a[i]), s[a[i]].insert(i);
        GG(i, find(a[i], i));
    }
    for(scanf("%d", &m); m; m --) {
        scanf("%d", &x);
        if(x == 0) {
            printf("%lld\n", query(1, 1, n, 2e9) - (ll)n * (n + 1) / 2);
        } else {
            scanf("%d %d", &x, &y);
            int l = *s[a[x]].begin(), r = *s[a[x]].rbegin();
            if(r > x) {
                r = *s[a[x]].upper_bound(x);
                if(l < x) {
                    l = *(-- s[a[x]].lower_bound(x));
                    GG(r, l);
                } else {
                    GG(r, 0);
                }
            } else {
                if(la[x]) p[la[x]] = n + 1, chan(1, 1, n, la[x]);
            }
            s[a[x]].erase(x);

            s[y].insert(x);
            l = *s[y].begin(), r = *s[y].rbegin();
            if(r > x) {
                r = *s[y].upper_bound(x);
                GG(r, x);
            } else {
                p[x] = n + 1; chan(1, 1, n, x);
            }
            if(l < x) {
                l = *(-- s[y].lower_bound(x));
                GG(x, l);
            } else {
                GG(x, 0);
            }

            a[x] = y;
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值