这一题一看就感觉可以跟前缀和联系起来,但是问题就是在不断的修改中可能会影响结果。
先是考虑从开始到i的种类和是多少,这时候只需要记录一下每个种类上一次出现的位置,若是出现过,就把之前那个在数组中删掉,在当前位置加上即可,用树状数组就可以动态的求出前缀和。树状数组就是一个板子,比较简单。
问题就是在处理到i + 1的时候再去求前i的种类和是会出现问题的,所以这个时候可以考虑一下离线算法,先把所有的问题输入之后,对问题集以右端点从小到大排序,当处理到i的时候看看是否有右端点为i的问题,这个时候解决这个问题是正确的,每次处理一个数之后就判断是否有以当前位置为右端点的问题即可。
当所有问题解决完之后,按照输入的顺序输出即可,只需要在输入的时候记录一下每个问题的顺序。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define x first
//#define y second
//#define int long long
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<int, string> pis;
const int mod = 1e9 + 7;
const int N = 1e6+ 10;
int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
int n, m;
int o[N];
int last[N];
typedef struct {
int a, b, i;
}aa;
aa p[N];
bool cmp(aa a, aa b)
{
return a.b < b.b;
}
int s[N * 4];
int lowbit(int a)
{
return a & -a;
}
void add(int a, int b)
{
for(int i = a; i <= n; i += lowbit(i))
s[i] += b;
return ;
}
int sum(int a)
{
int x = 0;
for(int i = a; i; i -= lowbit(i))
x += s[i];
return x;
}
int res[N];
inline void sovle()
{
cin >> n;
for(int i = 1; i <= n; i ++)
cin >> o[i];
cin >> m;
for(int i = 0; i < m; i ++)
{
cin >> p[i].a >> p[i].b;
p[i].i = i;
}
stable_sort(p, p + m, cmp); // 以右端点从小到大排序
int k = 0;
for(int i = 1; i <= n; i ++)
{
if(last[o[i]]){ // 出现过
add(last[o[i]], -1); // 删除之前的值
add(i, 1); // 加上当前的值
}
else { // 未出现
add(i, 1); // 加上当前的值
}
last[o[i]] = i;
while(k < m && p[k].b == i) // 解决以当前位置为右端点的所有问题
{
res[p[k].i] = sum(p[k].b) - sum(p[k].a - 1);
k ++;
}
}
for(int i = 0; i < m; i ++) // 按顺序输出所有的问题
{
cout << res[i] << endl;
}
}
signed main(void)
{
IOS;
int t = 1;
// cin >> t;
while(t --) sovle();
return 0;
}