树状数组(或线段树)离线
我们可以维护一个树状数组 (tree) ,计算 ([1,i]) 出现的不同种类的个数。然后根据前缀合思想,取 (ans=query(r)-query(l-1))
离线按 (r) 排序查询区间。
由于我们要求不重复,所以靠右的比靠左的更有价值(更关心靠右的)所以我们每次遇到一个重复的就可以把原来的清掉(靠左的已经被放进靠左的答案中了)。
是否重复的判断可以通过一个数组 (vis) 实现,同时 (vis) 还可以维护更新同一个数靠右的位置
线段树应该同理,但是既然有树状数组做法,为什么不写树状数组呢?
#include<bits/stdc++.h>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned long long
namespace Enterprise
{
inline int read()
{
int s = 0,f = 0;
rg char ch = getchar();
while( !isdigit(ch) ) f |= (ch == '-'),ch = getchar();
while( isdigit(ch) ) s = (s << 1) + (s << 3) + (ch ^ 48),ch = getchar();
return f ? -s : s;
}
#define lowbit(x) ((x)&(-x))
const int N=1e6+15;
struct lyy
{
int l,r,id;
inline bool operator < ( const lyy &rhs )const
{
return r < rhs.r;
}
};
lyy op[N];
int a[N],tree[N],ans[N],vis[N],n,m;
inline void add(int x,int v)
{
for( ; x <= n ; x += lowbit(x) )
tree[x]+=v;
}
inline int query(int x)
{
int ans=0;
for( ; x ; x -= lowbit(x) ) ans += tree[x];
return ans;
}
inline void main()
{
n = read();
for( rg int i = 1 ; i <= n ; i++ ) a[i] = read();
m = read();
for( rg int i = 1 ; i <= m ; i++ ) op[i].l = read(),op[i].r = read(),op[i].id = i;
sort(op + 1,op + m + 1);
for( rg int i = 1,j = 1 ; i <= m ; i++ )
{
for( ; j <= op[i].r ; j++ )
{
if(vis[a[j]]) add(vis[a[j]],-1);
add(j,1);
vis[a[j]] = j;
}
ans[op[i].id] = query(op[i].r) - query(op[i].l - 1);
}
for( rg int i = 1 ; i <= m ; i++ ) printf("%d
",ans[i]);
}
}
signed main()
{
Enterprise::main();
return 0;
}