题目大意
给一段长度n(n<=100000)序列,每次询问一段区间(个数<=200000),问区间中的数字种数。
题解
本题特别之处在于区间内的数字种数不满足区间的相加性,所以我们可以加约束条件。我们不妨考虑维护一个树状数组维护数字种数。显然当询问的r固定时,对于每一个数值,我们只保留其在[1,r]内最右侧的数字,将其它数字删除,这样对结果没有影响,反而数字的个数满足区间可加性了。所以定义树状数组维护的前缀和值为满足约束条件——对于每个数值只保留位置最右侧的数字——时,[1,r]中仍然存在的数字的个数。
因此,我们将所有询问按照右端点排序,当树状数组负责的右端点要向右延伸时,要在树状数组中把右端点的数值与其相等、位置位于它左侧的位置(解决的办法貌似叫做“链式前向星”?)对应前缀和更新-1,将右端点所在位置对应前缀和更新+1即可。
一定要注意查询是排过序的,输出时要按照原有的顺序输出!!!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_LEN = 500010, MAX_VAL = 1000010, MAX_QUERY = 200010;
int OrgData[MAX_LEN], EqValPrev[MAX_LEN];
int Len, TotQuery;
struct BIT
{
private:
int C[MAX_LEN];
int N;
int Lowbit(int x)
{
return x & -x;
}
public:
BIT(int n):N(n){}
void Update(int p, int val)
{
if (p == 0)
return;
while (p <= N)
{
C[p] += val;
p += Lowbit(p);
}
}
int Query(int p)
{
int ans = 0;
while (p >= 1)
{
ans += C[p];
p -= Lowbit(p);
}
return ans;
}
};
struct Query
{
int L, R, Ans;
}_qs[MAX_QUERY], *SortedQ[MAX_QUERY];
bool Cmp_R(Query *a, Query *b)
{
return a->R < b->R;
}
void GetEqValPrev()
{
static int valPrev[MAX_VAL];
for (int i = 1; i <= Len; i++)
{
EqValPrev[i] = valPrev[OrgData[i]];
valPrev[OrgData[i]] = i;
}
}
int main()
{
scanf("%d", &Len);
for (int i = 1; i <= Len; i++)
scanf("%d", OrgData + i);
scanf("%d", &TotQuery);
for (int i = 1; i <= TotQuery; i++)
scanf("%d%d", &_qs[i].L, &_qs[i].R);
static BIT g(Len);
for (int i = 1; i <= TotQuery; i++)
SortedQ[i] = _qs + i;
sort(SortedQ + 1, SortedQ + TotQuery + 1, Cmp_R);
GetEqValPrev();
int curR = 0;
for (int i = 1; i <= TotQuery; i++)
{
while (curR < SortedQ[i]->R)
{
curR++;
g.Update(EqValPrev[curR], -1);
g.Update(curR, 1);
}
SortedQ[i]->Ans = g.Query(SortedQ[i]->R) - g.Query(SortedQ[i]->L - 1);
}
for (int i = 1; i <= TotQuery; i++)
printf("%d
", _qs[i].Ans);
return 0;
}