FJUT3097区间数种类(思维+树状数组+离线)

版权声明:Please work hard for your dreams. https://blog.csdn.net/calculate23/article/details/87701266

题目链接fjut3097

题目大意:RT,大致就是给你n长度的数字序列,以及q组含左右端点的区间查询,问区间内的数字种类

解题思路:我们利用C数组表示从1~i的区间的数字的种类数,这样就可以通过getsum(right) - getsum(left-1)求得区间[left,right]的情况。但问题就转化成如何维护这个C数组,这里我们发现,但凡某个数字在k位置之前还发现有一个跟它一样(假设这个位置为x)的,如果我们此时在这个位置+1,这样对于数字val[k]会有val[k] == val[x] (x< k)。如此一来前缀就有两个这个数的贡献,这是我们要删除一个,删除哪个和我们判断这些询问的顺序有关,这里举个例子观察一下:{1,2,3,2,4,5},比如我们查询[1,6];[1,3];[3,6],我们知道结果分别是5,3,5。但是如果做这样一个操作:每次查询的时候发现前面有重复的出现,vis[val[i]]!=0(vis存上一次数字出现的下标),就执行add(vis,-1),否则就add(vis,1)。这样就相当于出现了重复的数字删除了前一个数字的贡献。但是可能会出现这样的bug(我先查[1,6]:[1,3]区间那个数字2的贡献被我们删除了,然后查询[3,6]肯定包含了2的贡献这毫无疑问。但是如果之后再查询[1,3]或者是包含我们删除贡献那个数字且不包含我们后面与之重复的数字的区间,一定是错误的。)这时,我们如果就是先把这种区间先查询,然后再查询那种可能重复的区间,就解决了,实现就是把查询区间先按r排个序。同理,我们也可以删除后面那个,但是操作和这种镜像。


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#include <deque>
#include <queue>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <utility>

using namespace std;
const int maxn = (int)1e5+5;

struct Node {
	int l,r; // 查询的区间
	int id; // 查询的组号
	bool operator < (const Node& A) const {
		return A.r > this->r;
	}
};

int c[maxn],val[maxn],n,q,ans[maxn];
Node qry[maxn];
map<int, int > vis;

template<typename E>
inline E lowbit(E x) {
	return x & (-x);
}

void add (int idx, int num) {
	while (idx <= n) {
		c[idx] += num;
		idx += lowbit(idx);
	}
}

int get_sum (int idx) {
	int res = 0;
	while (idx > 0) {
		res += c[idx];
		idx -= lowbit(idx);
	}
	return res;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	while (cin >> n >> q) {
		for (int i = 1; i <= n; i++) {
			cin >> val[i];
		}
		for (int i = 1; i <= q; i++) {
			cin >> qry[i].l >> qry[i].r;
			qry[i].id = i;
		}
		sort(qry + 1, qry + 1 + q);//保证每次查询时右限都不小于之前的查询
		memset(c, 0, sizeof(c));
		vis.clear();
		
		int point = 1, limit;
		for (int i = 1; i <= q; i++) {
			limit = qry[i].r;
			while (point <= limit) {  //把之前出现过的数字更新
				if(vis.find(val[point]) != vis.end()) {
					add(vis[val[point]], -1);
				}
				vis[val[point]] = point;
				add(vis[val[point]], 1);
				point ++;
			}
			ans[qry[i].id] = get_sum(limit) - get_sum(qry[i].l - 1);
		}
		
		for (int i = 1; i <= q; i++) {
			cout << ans[i] << '\n';
		}
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/calculate23/article/details/87701266