NOI模拟(10.29)T3 颜色

颜色

题目背景:

10.29 NOI模拟T3

分析:树状数组 + 线段树 + set or 带修莫队 or 分块

 

第一眼带修莫队,思索了一下觉得貌似复杂度真的非常不稳啊······选择另辟蹊径,然后发现了比较稳的树套树做法,首先我们对于每一个位置记录其pre[i],表示上一个出现颜色c[i]的位置是pre[i],那么显然,对于一段区间l ~ r的询问,答案就是所有pre小于l - 1的地方的权值和,这个可以考虑用主席树维护,好了,如果没有修改操作的话,我们就可以用主席树一个log搞过去了,现在考虑修改操作,为了让修改不是nlog,那么我们直接在外层套一个树状数组,这样可以是修改和询问全部变成log2n,然后考虑修改的具体操作,显然我们需要快速查找某一位置的上一个颜色x出现的位置和下一个颜色x的出现位置,我们直接对每一个颜色开一个set,然后每一次lower_bound查询位置即可。

Source:

/*
	created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>

const int MAXN = 100000 + 10;

int n, m, t, x, y, cnt;
int c[MAXN], w[MAXN], last[MAXN];
std::set<int> color[MAXN];
std::set<int> :: iterator it, pre, suc;
int root[MAXN];

struct node {
	int left, right, sum;
} tree[MAXN * 300];

inline void read_in() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) scanf("%d", &c[i]);
	for (int i = 1; i <= n; ++i) scanf("%d", &w[i]);
}

inline void insert(int &cur, int l, int r, int pos, int w) {
	if (cur == 0) cur = ++cnt;
	tree[cur].sum += w;
	if (l == r) return ;
	int mid = l + r >> 1;
	if (pos <= mid) insert(tree[cur].left, l, mid, pos, w);
	else insert(tree[cur].right, mid + 1, r, pos, w);
}

inline int query(int cur, int l, int r, int ql, int qr) {
	if (ql <= l && r <= qr) return tree[cur].sum;
	int mid = l + r >> 1;
	int ret = 0;
	if (ql <= mid) ret += query(tree[cur].left, l, mid, ql, qr);
	if (qr > mid) ret += query(tree[cur].right, mid + 1, r, ql, qr);
	return ret;
}

inline int lowbit(int i) {
	return i & -i;
}

inline void insert(int i, int pos, int w) {
	for (; i <= n; i += lowbit(i)) insert(root[i], 0, n, pos, w);
}

inline void query(int l, int r) {
	int ans = 0;
	for (int i = r; i > 0; i -= lowbit(i)) 
		ans += query(root[i], 0, n, 0, l - 1);
	for (int i = l - 1; i > 0; i -= lowbit(i))
		ans -= query(root[i], 0, n, 0, l - 1);
	printf("%d\n", ans);
}

inline void modify(int pos, int x) {
	int u = c[pos];
	if (u == x) return ;
	it = pre = suc = color[u].lower_bound(pos), pre--, suc++;
	insert(pos, *pre, -w[u]);
	if (suc != color[u].end()) 
		insert(*suc, pos, -w[u]), insert(*suc, *pre, w[u]);
	color[u].erase(it), color[x].insert(pos), c[pos] = x;
	it = pre = suc = color[x].lower_bound(pos), pre--, suc++;
	insert(pos, *pre, w[x]);
	if (suc != color[x].end()) 
		insert(*suc, *pre, -w[x]), insert(*suc, pos, w[x]);
	
}

inline void solve_tree() {
	for (int i = 1; i <= n; ++i) color[i].insert(0);
	for (int i = 1; i <= n; ++i)
		color[c[i]].insert(i), insert(i, last[c[i]], w[c[i]]), last[c[i]] = i;
}

inline void solve_query() {
	while (m--) {
		scanf("%d%d%d", &t, &x, &y);
		if (t == 1) modify(x, y);
		else query(x, y);
	}
}

int main() {
	freopen("color.in", "r", stdin);
	freopen("color.out", "w", stdout);
	read_in();
	solve_tree();
	solve_query();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值