0
点赞
收藏
分享

微信扫一扫

AtCoder Beginner Contest 174

小编 2022-04-13 阅读 71

F - Range Set Query

树状数组+离线处理

题意:给n个颜色,询问L~R区间内出现了几种颜色。

思路:(很简单耐心看完)这题可以使用可持久化线段树,代码太多了不考虑了。使用树状数组就是求前缀和,那么问题来了: 如何求前缀和的时候不重复计算多次出现的颜色,保证重复的颜色贡献只有1?
我的做法是开pos数组记录这个颜色上一次出现的位置,每次有重复颜色时,就把上一次出现的颜色的贡献清零,只计算最后一次出现的颜色的贡献。但这时候需要保证需要离线处理询问,将其按右端点R排序,每一次查询的时候,重复元素的贡献值不会出现在右端点R的右边。
再使用一个额外的pos数组记录上一次出现的重复颜色的下标就行,使用树状数组将上一次重复出现的位置-1,最新的位置+1,query函数得到的前缀和一定是每个颜色最多一次贡献。

在这里插入图片描述

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 5e5+10;
int pos[N], c[N], a[N], nowPos = 1;
int n, q;

struct Node
{
    int first, second, id, ans;
} qs[N];


int query(int x)
{
	int res = 0;
	for(;x;x-=x&(-x)) res += c[x];
	return res;
}

void insert(int x, int d)
{
	for(;x<=n;x+=x&(-x)) c[x] += d;
}

bool cmp(Node a, Node b)
{
	return a.second < b.second;
}

bool cmp2(Node a, Node b)
{
    return a.id < b.id;
}

signed main()
{
	cin >> n >> q;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i];
	}
	for(int i = 1; i <= q; i ++)
	{
		cin >> qs[i].first >> qs[i].second;
        qs[i].id = i;
	}
    
	sort(qs+1, qs+1+q, cmp);
	for(int i = 1; i <= q; i ++)
	{
		for(int j = nowPos; j <= qs[i].second; j ++)
		{

			if(pos[a[j]] == 0)
			{
				insert(j, 1);
				pos[a[j]] = j;
			}
			else
			{
				insert(pos[a[j]], -1);
				insert(j, 1);
				pos[a[j]] = j; 
			}
		}
		nowPos = qs[i].second + 1;
        
        // 这样直接输出是不对的啊
        // 排序之后就不是原来的询问顺序了
        // cout << query(qs[i].second) - query(qs[i].first-1) << endl;

        qs[i].ans = query(qs[i].second) - query(qs[i].first-1);
	}
    sort(qs+1, qs+1+q, cmp2);
    for(int i = 1; i <= q; i ++)
    {
        cout << qs[i].ans << endl;
    }
	return 0;
}
举报

相关推荐

0 条评论