【传送门:BZOJ2743】
简要题意:
给出一个长度为n的数列,总共有c种不同的数,给出m个询问,每个询问输入l,r,输出数列中l到r中出现的次数大于等于两次的数的个数
题解:
我们还是用离线+树状数组的方法来做
一样设next[i]为第i位置上的数下一次出现的位置
但是说了是改版,当然做法会有所不同
因为我们不能直接把第一次出现的数认定为会出现两次及以上,所以我们先将所有第二次出现的数++
而且我们在做这道题的时候要尽量保证数列的树状数组的值要放在现存的出现的第二次的数的位置上
然后就开始从左往右扫一遍数列
假如当前扫到了x位置,如果x现在的值是1的话,说明如果删掉x会对后面的数产生影响,就将a[x]--,如果next[next[x]]存在的话,a[next[next[x]]++
如果x的值为0的话,那么如果next[x]存在的话,a[next[x]]必定为1(因为我们每次++都会在下下次出现的位置操作),那么就将a[next[x]]--,a[next[next[x]]++
因为我们要判断x位置是否为1,我们用d数组来保存只对于每个位置来说的值,有利于我们判断
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; struct question { int l,r,d,id; }q[1100000]; int c[1100000],k[1100000]; int next[1100000]; int a[1100000]; int lowbit(int x){return x&-x;} int n; void change(int x,int c) { while(x<=n) { a[x]+=c; x+=lowbit(x); } } int getsum(int x) { int ans=0; while(x!=0) { ans+=a[x]; x-=lowbit(x); } return ans; } bool cmp(question n1,question n2) { return n1.l<n2.l; } bool cmpi(question n1,question n2) { return n1.id<n2.id; } int d[1100000]; int main() { int C,m; scanf("%d%d%d",&n,&C,&m); for(int i=1;i<=n;i++) scanf("%d",&k[i]); memset(next,0,sizeof(next)); memset(c,0,sizeof(c)); for(int i=n;i>=1;i--) { next[i]=c[k[i]]; c[k[i]]=i; } memset(a,0,sizeof(a)); memset(d,0,sizeof(d)); for(int i=1;i<=C;i++) { if(c[i]!=0&&next[c[i]]!=0) { change(next[c[i]],1); d[next[c[i]]]++; } } for(int i=1;i<=m;i++) { scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+m+1,cmp); int l=1; for(int i=1;i<=m;i++) { while(l<q[i].l) { if(d[l]!=0){change(l,-1);d[l]--;} else if(next[l]!=0){change(next[l],-1);d[next[l]]--;} if(next[next[l]]!=0){change(next[next[l]],1);d[next[next[l]]]++;} l++; } q[i].d=getsum(q[i].r)-getsum(q[i].l-1); } sort(q+1,q+m+1,cmpi); for(int i=1;i<=m;i++) printf("%d ",q[i].d); return 0; }