我的思维能力果然在提高呢
原题:

n<=10^6
区间不同种类颜色数,看上去很棘手啊
普通的线段树思想是做不了的,因为区间不同种类颜色数这玩意没法进行合并
那必须转化思维角度
中间各种错误的处理方式不赘述了hhh
最后还是偶然碰到了正解
这题没有修改操作,那可以离线呀,区间按左端点排序
然后惊喜地发现,按左端点递增的顺序枚举区间,那么某个区间左边的颜色都不考虑
右边的颜色我们只考虑离左端点最近的
因为如果更远的颜色在区间内,那么更近的相同颜色一定在区间内
所以一开始线段树里所有颜色第一次出现的位置为1,其他为0
按左端点递增枚举区间,每次区间左端点+1,就对于原先在左端点的颜色,找到它在序列中的后一个位置,在线段树中把这个位置权值设为1
表示我们开始考虑这个位置上的颜色
然后求查询区间的和
总结一下,这题本质是强调性质:求区间不同颜色个数,只需要考虑在左端点之后的所有颜色中,第一个出现的颜色,统计区间中有多少个这样的颜色
然后题目没有操作的性质能让我们离线排序询问区间,使得能对左端点之后的所有颜色进行操作
代码:

1 #include<iostream>
2 #include<cstdio>
3 #include<algorithm>
4 using namespace std;
5 int rd(){int z=0,mk=1; char ch=getchar();
6 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();}
7 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();}
8 return z*mk;
9 }
10 struct nds{int x,y,z;}b[1100000];
11 int n,m,a[1100000];
12 int nxt[1100000],lst[1100000];
13 int v[4100000];
14 int ans[11000000];
15 void mdf(int x,int l,int r,int y,int z){
16 if(l==r){
17 v[x]+=z;
18 return ;
19 }
20 int md=(l+r)>>1;
21 if(y<=md) mdf(x<<1,l,md,y,z);
22 else mdf(x<<1|1,md+1,r,y,z);
23 v[x]=v[x<<1]+v[x<<1|1];
24 }
25 int qry(int x,int l,int r,int ql,int qr){
26 if(l==ql && r==qr) return v[x];
27 int md=(l+r)>>1;
28 if(ql<=md && qr>md) return qry(x<<1,l,md,ql,md)+qry(x<<1|1,md+1,r,md+1,qr);
29 else if(qr<=md) return qry(x<<1,l,md,ql,qr);
30 else return qry(x<<1|1,md+1,r,ql,qr);
31 }
32 bool cmp(nds x,nds y){ return x.x==y.x ? x.y<y.y : x.x<y.x;}
33 int main(){
34 cin>>n;
35 for(int i=1;i<=n;++i){
36 a[i]=rd();
37 if(lst[a[i]]) nxt[lst[a[i]]]=i;
38 else mdf(1,1,n,i,1);
39 lst[a[i]]=i;
40 }
41 cin>>m;
42 for(int i=1;i<=m;++i) b[i].x=rd(),b[i].y=rd(),b[i].z=i;
43 sort(b+1,b+m+1,cmp);
44 int tmp=0;
45 for(int i=1;i<=m;++i){
46 for(;tmp<b[i].x;++tmp)if(nxt[tmp])
47 mdf(1,1,n,nxt[tmp],1);
48 ans[b[i].z]=qry(1,1,n,b[i].x,b[i].y);
49 }
50 for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
51 return 0;
52 }
