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