1 5 2 3 1 2 5 4 1 5 2 4
1 2
题意为询问一段区间里的数能组成多少段连续的数。先考虑从左往右一个数一个数添加,考虑当前添加了i - 1个数的答案是x,那么添加完i个数后的答案是多少?可以看出,是根据a[i]-1和a[i]+1是否已经添加而定的,如果a[i]-1或者a[i]+1已经添加一个,则段数不变,如果都没添加则段数加1,如果都添加了则段数减1。设v[i]为加入第i个数后的改变量,那么加到第x数时的段数就是sum{v[i]} (1<=i<=x}。仔细想想,若删除某个数,那么这个数两端的数的改变量也会跟着改变,这样一段区间的数构成的段数就还是他们的v值的和。将询问离线处理,按左端点排序后扫描一遍,左边删除,右边插入,查询就是求区间和。
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define prt(k) cout<<#k"="<<k<<endl; #include<algorithm> #define N 200033 int c[N],a[N]; int pos[N]; int ans[N]; struct node { int id,l,r; }; bool cmp(node a,node b) { return a.r>b.r; } int lowbit(int x) { return x&-x; } void update(int x,int v) { while(x<N) { c[x]+=v; x+=lowbit(x); } } int query(int x) { int r=0; while(x>0) { r+=c[x]; x-=lowbit(x); } return r; } node q[N]; int n,m; bool vis[N]; int main() { int tt; scanf("%d",&tt); while(tt--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",a+i),pos[a[i]]=i; memset(vis,0,sizeof vis); memset(c,0,sizeof c); for(int i=0;i<m;i++) { scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q,q+m,cmp); for(int i=n;i>=1;i--) { int d=0; //vis[a[i]-1]+vis[a[i]+1]; if(vis[a[i]-1]) d++; if(vis[a[i]+1]) d++; if(d==0) update(i,1); if(d==2) update(i,-1); vis[a[i]]=1; } int j=n; for(int i=0;i<m;i++) { for(;j>q[i].r;j--) { if(a[j]>1&&pos[a[j]-1]<j) { update(pos[a[j]-1],1); } if(a[j]<n&&pos[a[j]+1]<j) { update(pos[a[j]+1],1); } } ans[q[i].id]=query(q[i].r)-query(q[i].l-1); } for(int i=0;i<m;i++) printf("%d\n",ans[i]); } }