题意
求一段区间中出现至少2次的数的个数。
保证n,m,c≤2∗106,c是值域
题解
上课讲的时候觉得有些熟悉,后来才想起来就是HH的项链的变式,觉得这思路还是值得记一下。
第一种就是莫队做法,操作很简单,但是会超时。
比较巧妙的思路就是,对于右端点相同的查询,如果一个数出现了至少两次,那么我们只关心他第二次出现的位置,因为如果包含他第3次或者更多次出现的位置,就一定包含他,而且只需两次就可以有贡献,多了贡献也不会增加。
以序列位置为下标建立树状数组,对于从右往左出现第二次的数,在这个位置+1;
对于同一个右端点的查询,就是查询区间和。
当右端点移动时,这个数成为第一次出现的数;对于之前第一次出现的数就变成了第二次出现的数,对于该位置+1;之前第二次出现的数,这时他变成第三次出现已经没有任何贡献,这个位置-1;
#include<bits/stdc++.h> using namespace std; const int maxn=2000005; int n,m,c; int pos[maxn],size; int co[maxn],fir[maxn],sec[maxn]; int cx[maxn<<1]; int ans[maxn]; struct question{ int l,r,id; }q[maxn]; template<class T>inline void read(T &x){ x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} } bool cmp(question a,question b){return a.r<b.r;} void add(int x,int val){for(;x<=n;x+=x&-x) cx[x]+=val;} int query(int x){ int ret=0; for(;x;x-=x&-x) ret+=cx[x]; return ret; } int main(){ read(n);read(c);read(m); for(int i=1;i<=n;i++) read(co[i]); for(int i=1;i<=m;i++){read(q[i].l);read(q[i].r);q[i].id=i;} sort(q+1,q+m+1,cmp); int j=1; for(int i=1;i<=n;i++){ if(!fir[co[i]])//这个数之前没出现 fir[co[i]]=i; else if(!sec[co[i]]){//只有一个 add(fir[co[i]],1); sec[co[i]]=fir[co[i]]; fir[co[i]]=i; } else {//出现了两次以上 add(sec[co[i]],-1); add(fir[co[i]],1); sec[co[i]]=fir[co[i]]; fir[co[i]]=i; } while(q[j].r==i) ans[q[j].id]=query(q[j].r)-query(q[j].l-1),j++; } for(int i=1;i<=m;i++) printf("%d ",ans[i]); }