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;
}