传送门
【题目分析】
刚看完这道题YY了一下线段树的做法,大概就是维护颜色出现次数,如果区间合并时一个颜色的个数从0变为1,那么就出现了一次,cnt++(不过感觉很难写所以就没写)
很明显可以离线来做,因为不涉及修改操作,所以将所有询问按左右id进行排序,对于同一颜色,用链表储存位置,从左向右走的时候向树状数组中更新,然后在树状数组中查询即可。(结果数组开小了调了半天233)
【代码~】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=1e6+10;
LL n,q,maxx;
LL a[MAXN],tr[MAXN];
LL head[MAXN],nxt[MAXN];
LL ans[MAXN];
struct ask{
LL l,r;
LL id;
friend inline bool operator<(const ask &a,const ask &b){
if(a.l==b.l)
return a.r<b.r;
return a.l<b.l;
}
}quer[MAXN];
LL Read()
{
LL i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
LL lowbit(LL x)
{
return x&(-x);
}
void update(LL x,LL v)
{
for(register int i=x;i<=n;i+=lowbit(i))
tr[i]+=v;
}
LL query(LL x)
{
LL ret=0;
for(register int i=x;i;i-=lowbit(i))
ret+=tr[i];
return ret;
}
int main()
{
n=Read();
for(register int i=1;i<=n;++i)
a[i]=Read(),maxx=max(maxx,a[i]);
for(register int i=n;i>=1;--i)
{
nxt[i]=head[a[i]];
head[a[i]]=i;
}
for(register int i=1;i<=maxx;++i)
{
if(head[i])
update(head[i],1);
}
q=Read();
for(register int i=1;i<=q;++i)
{
quer[i].l=Read(),quer[i].r=Read();
quer[i].id=i;
}
sort(quer+1,quer+q+1);
LL last=1;
for(register int i=1;i<=q;++i)
{
while(last<quer[i].l)
{
if(nxt[last])
update(nxt[last],1);
last++;
}
ans[quer[i].id]=query(quer[i].r)-query(quer[i].l-1);
}
for(register int i=1;i<=q;++i)
cout<<ans[i]<<'\n';
return 0;
}