uva12345

今天心血来潮学习了一下主席树(可持久化线段树),于是就拿这题来练手了。发现其实还挺好写的,至少比之前写的树套树短多了。

PS:主席树虽然比较好写,速度也很快,但是内存实在是伤不起。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;

const int N = 50005;

set <int> a[1000005];
typedef set<int>::iterator IT;

struct vertice {
    int left, right, data;
} tree[N * 200];

int root[N], next[N], b[N];
int tot;
int n;

int lowbit(int x) { return x & -x; }

namespace Nsolve {
    
    void new_vertice(int &k) {
	k = ++tot;
    }

    void add(int &k, int m, int n, int x, int s) {
	if (m > x || n < x) return ;
	if (k == 0) new_vertice(k);
	if (m == n) {
	    tree[k].data += s;
	    return ;
	}
	int mid = m + n >> 1;
	add(tree[k].left, m, mid, x, s);
	add(tree[k].right, mid + 1, n, x, s);
	tree[k].data = 0;
	if (tree[k].left) tree[k].data += tree[tree[k].left].data;
	if (tree[k].right) tree[k].data += tree[tree[k].right].data;
    }
    
    void addpoint(int x, int y, int s) {
	for (int j = x; j <= n; j += lowbit(j))
	    add(root[j], 0, n, y, s);
    }

    void modify(int x, int y) {
	IT t1 = a[b[x]].upper_bound(x);
	IT t2 = a[b[x]].lower_bound(x);
	int t = 0;
	if (t2 != a[b[x]].begin()) {
	    --t2;
	    t = *t2;
	}
	addpoint(x, t, -1);
	if (t1 != a[b[x]].end()) {
	    addpoint(*t1, x, -1);
	    addpoint(*t1, t, 1);
	} 
	a[b[x]].erase(x);
	b[x] = y;
	a[y].insert(x);
	t1 = a[y].upper_bound(x);
	t2 = a[y].lower_bound(x);
	t = 0;
	if (t2 != a[y].begin()) {
	    --t2;
	    t = *t2;
	}
	if (t1 != a[y].end()) {
	    addpoint(*t1, t, -1);
	    addpoint(*t1, x, 1);
	}
	addpoint(x, t, 1);
    }

    int query(int k, int m, int n, int l, int r) {
	if (m > r || n < l) return 0;
	if (m >= l && n <= r) return tree[k].data;
	int mid = m + n >> 1;
	return query(tree[k].left, m, mid, l, r) +
	    query(tree[k].right, mid + 1, n, l, r);
    }

    void solve(int n, int q) {
	while (q--) {
	    int ch;
	    while (ch = getchar(), ch != 'M' && ch != 'Q');
	    if (ch == 'M') {
		int x, y;
		scanf("%d%d", &x, &y);
		++x;
		modify(x, y);
		continue;
	    }
	    int ans = 0;
	    int l, r;
	    scanf("%d%d", &l, &r);
	    ++l;
	    for (int i = r; i; i -= lowbit(i))
		ans += query(root[i], 0, n, 0, l - 1);
	    for (int i = l - 1; i; i -= lowbit(i))
		ans -= query(root[i], 0, n, 0, l - 1);
	    printf("%d\n", ans);
	}
    }
};

namespace Ninit {
    
    void init(int n) {
	for (int i = 1; i <= n; ++i) {
	    int x;
	    scanf("%d", &x);
	    a[x].insert(i);
	    IT it = a[x].lower_bound(i);
	    int t = 0;
	    if (it != a[x].begin()) {
		--it;
		t = *it;
	    }
	    b[i] = x;
	    Nsolve::addpoint(i, t, 1);
	}
    }
};

int main() {
    int q;
    scanf("%d%d", &n, &q);
    Ninit::init(n);
    Nsolve::solve(n, q);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值