bzoj2120树状数组套主席树解法

2120: 数颜色

Time Limit: 6 Sec   Memory Limit: 259 MB
Submit: 5448   Solved: 2174
[ Submit][ Status][ Discuss]

Description

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?

Input

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

Output

对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

Sample Input

6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6

Sample Output

4
4
3
4

HINT

对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。


2016.3.2新加数据两组by Nano_Ape

这题是分块和莫队的板,不过听说可以树状主席套板,就屁颠屁颠地跑过来打板了。(然而并没有想象得那么简单)
首先把颜色去重排序种种
然后,记录每个颜色的前一种相同颜色的位置,假设为head数组。
l~r中有多少种不同的颜色,就是有多少种l~r中的颜色的前一种颜色的位置在l之前(证明:如果一种颜色有在这段区间内有重复,那么这种颜色在此区间内的第一个的前一个颜色一定在l之前,其余一定在l之后,也就是说每种颜色只被计算了一次)
换一句话说,本题被转化为计算l~r区间的head数组中有多少个数小于l,那么就是主席树模板了
修改稍微有点麻烦
考虑一次修改,会影响的是这个位置pos对应的head,还有pos位置颜色下一次出现颜色next的head(因为是链式结构)
这个问题就是在一个序列中快速寻找第k大的元素,并且找到k之前之后的元素进行修改。可以用平衡树来维护
然而平衡树写不动怎么办,一个set扔过去。据体来说,每种颜色建一个set,记录它们出现的位置,修改时用lower_bound快速寻找,找到后修改主席树,然后一个erase一个insert,然后再在新的颜色那里进行同样的操作(STL大法好)

呼呼,搞完啦!

贴个代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#include<set>
#define maxn 11005
using namespace std;
int read()
{
	char ch = getchar(); int x = 0, f = 1;
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}
char read2()
{
	char ch = getchar();
	while(ch != 'Q' && ch != 'R') ch = getchar();
	return ch; 
}

int lowbit(int x) {return x & -x;}
int N, n, m, top;
int s[maxn], root[maxn], b[maxn], c[maxn], head[maxn];
set<int>col[maxn];
set<int> :: iterator pre, it, next;


struct Tree {
	int l, r, sum;
}t[maxn * 100];

struct Ques {
	int x, y;
	char ch;
	void init()
	{
		ch = read2();
		if(ch == 'Q') {x = read(); y = read();}
		else {x = read(); b[++n] = y = read();}
	}
}q[maxn];

void build_tree(int &p, int L, int R)
{
	t[p = ++top].sum = 0;
	if(L == R) return;
	int mid = L + R >> 1;
	build_tree(t[p].l, L, mid);
	build_tree(t[p].r, mid + 1, R);
}

void add_tree(int &cur_p, int pre_p, int pos, int add, int L, int R)
{
	t[cur_p = ++ top] = t[pre_p];
	t[cur_p].sum += add;
	if(L == R) return;
	int mid = L + R >> 1;
	if(pos <= mid) add_tree(t[cur_p].l, t[pre_p].l, pos, add, L, mid);
	else add_tree(t[cur_p].r, t[pre_p].r, pos, add, mid + 1, R);
}

void creat_tree(int p, int pos, int add)
{
	while(p <= N)
	{
		int P;
		add_tree(P, s[p], pos, add, 0, n);
		s[p] = P;
		p += lowbit(p);
	}
}

int sum(int p, int L, int R, int k)
{
	if(L == R) return t[p].sum;
	int mid = L + R >> 1;
	if(k <= mid) return sum(t[p].l, L, mid, k);
	else return t[t[p].l].sum + sum(t[p].r, mid + 1, R, k);
}

int query(int pos, int k)
{
	int ans = sum(root[pos], 0, n, k);
	for(int i = pos; i; i -= lowbit(i)) ans += sum(s[i], 0, n, k);
	return ans;
}

void init()
{
	n = N = read(); m = read();
	for(int i = 1;i <= N; ++i) b[i] = c[i] = read();
	for(int i = 1;i <= m; ++i) q[i].init();
	sort(b + 1, b + n + 1);
	n = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1;i <= n; ++i) col[i].insert(0);
	build_tree(root[0], 0, n);
	for(int i = 1;i <= N; ++i) s[i] = root[0];
	for(int i = 1;i <= N; ++i)
	{
		c[i] = lower_bound(b + 1, b + n + 1, c[i]) - b;
		add_tree(root[i], root[i - 1], head[c[i]], 1, 0, n);
		head[c[i]] = i;
		col[c[i]].insert(i);
	}
}

void change(int pos, int nc)
{
	nc = lower_bound(b + 1, b + n + 1, nc) - b;
	int oc = c[pos];
	pre = next = it = col[oc].lower_bound(pos);
	pre--; next++;
	creat_tree(pos, *pre, -1);
	if(next != col[oc].end()) creat_tree(*next, pos, -1), creat_tree(*next, *pre, 1);
	col[oc].erase(it); col[nc].insert(pos); c[pos] = nc;
	pre = next = it = col[nc].lower_bound(pos);
	pre--; next++;
	creat_tree(pos, *pre, 1);
	if(next != col[nc].end()) creat_tree(*next, pos, 1), creat_tree(*next, *pre, -1);
}

void solve()
{
	for(int i = 1;i <= m; ++i)
	{
		if(q[i].ch == 'Q') printf("%d\n", query(q[i].y, q[i].x - 1) - query(q[i].x - 1, q[i].x - 1));
		else change(q[i].x, q[i].y);
	}
}

int main()
{
	init();
	solve();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值