【HEOI2012】采花

题面来自洛谷

  • 首先非常显然的 答案=颜色总数-数量为1的颜色数

  • 如果只有一次询问:

    • 考虑如何算颜色总数:
      如果将每一个颜色在当前询问的最右边的位置赋为1,其余位置赋为0的话,是不是就是一个区间和呢?
      用查询数组1表示。

    • 考虑如何算数量为1的颜色数:
      如果将每一个颜色在当前区间的位置赋为1,该颜色的前一个位置赋为-1,其余位置赋为0,是不是这又变成了一个区间和呢?
      用查询数组2表示。

    • 为什么这是对的呢:
      如果该区间包含了多个x的话其实只用考虑最后两个即可,倒数第三个第四个无关紧要,完全可以缩为0。

  • 接下来考虑多次询问:

    • 如何如果右端点r固定每次将左端点l右移:
      问题就非常简单了吧,只要将右端点所包括的点做成单次询问的查询数组即可,左端点的右移仅仅会影响询问而不会影响查询数组的值。
    • 如果右端点会不断右移呢:
      我们只要使得查询数组仍然成立即可。
      如果每次添加一个点x则将查询数组1中x的上一个位置赋为0,当前位置赋为1;将查询数组2中x的上上个位置赋为0,上个位置赋为-1,当前位置赋为1即可
    • 如何保证上述条件呢:
      将问题离线,以右端点r为关键字排序即可。
  • 注意点:由于n的范围所以要用树状数组来维护区间和。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline long long _read() {
	long long ans; char c;
	for (c = getchar(); c<'0' || c>'9'; c = getchar());
	for (ans = 0; c <= '9'&&c >= '0'; c = getchar()) ans = ans * 10 + c - '0';
	return ans;
}
long long write_node[20], write_i;
inline void _write(long long ans) {
	if (!ans) putchar('0');
	for (write_i = 1; ans; write_i++, ans /= 10) write_node[write_i] = ans % 10;
	for (write_i--; write_i; write_i--) putchar(write_node[write_i] + '0');
	putchar('\n'); return;
}
struct TT {
#define lowbit(x) x&(-x)
	int a[2000001], n;
	inline void inin(int x) {
		n = x;
		memset(a, 0, sizeof a);
		return;
	}
	inline int ask(int pos) {
		register int ans = 0;
		for (register int i = pos; i; i -= lowbit(i)) ans += a[i];
		return ans;
	}
	inline void change(int pos, int tag) {
		if (pos == 0) return;
		for (register int i = pos; i <= n; i += lowbit(i)) a[i] += tag;
		return;
	}
}f1, f2; //查询数组
struct TTT {
	int l, r, pos;
}t[2000001];
int n, q;
int pre[2000001], last[2000001], a[2000001], ans[2000001];
inline bool cmd(TTT t1, TTT t2) {
	return t1.r < t2.r;
}
inline void inin() {
	n = _read(); q = _read(); q = _read();
	for (register int i = 1; i <= n; ++i) a[i] = _read();
	memset(last, 0, sizeof last);
	for (register int i = 1; i <= n; ++i) pre[i] = last[a[i]], last[a[i]] = i;
	for (register int i = 0; i < q; ++i) t[i].l = _read(), t[i].r = _read(), t[i].pos = i;
	sort(t, t + q, cmd);
	return;
}
int main() {
	inin();
	f1.inin(n); f2.inin(n);
	for (register int i = 0, r = 1; i < q; ++i) {
		while (r <= t[i].r&&r <= n) {
			f1.change(pre[r], -1);
			f1.change(r, 1);
			f2.change(pre[pre[r]], 1);
			f2.change(pre[r], -2);
			f2.change(r, 1);
			++r;
		}
		ans[t[i].pos] = f1.ask(t[i].r) - f1.ask(t[i].l - 1) - f2.ask(t[i].r) + f2.ask(t[i].l - 1);
	}
	for (register int i = 0; i < q; ++i) _write(ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值