<题面>
卡常终究比不上算法的优化……
这是莫队的有点小坑的题,
首先不一定能想到,想到不一定打对,打对不一定打好。
首先你会发现,这个题的时限是很长的~
$n$和$m$也是很大的。
于是我们可以计算一个时间复杂度,顺便搞一个块长。
首先有$Theta (M)$的询问,设块长为$L$,
于是左端点移动,不跨块,移动不超过$L$
右端点移动$N$
然后就有左面的总共移动$M imes L $
右面的移动$frac{N}{L} imes N$这里应该就是说,每次要挪一个 $N$,而左端点只挪$frac{N}{L}$次。
(转移一会再算)
这样莫队总复杂度:$Theta(M imes L + frac{N^2}{L})$
所以当块长为$sqrt{frac{N^2}{M}}=frac{N}{sqrt{M}}$时复杂度是最优的(基本不等式)
然后我们想转移,他让我们求一个区间数的个数。
那么,我们就可以用个数据结构,来维护$[a,b]$中数的个数和去重后的数个数
仔细算下,$log N$的时间复杂度还能承受。
所以选择树状数组或是线段树(常数有点大,用zkw好一些(但是用线段树的A的很困难~))
维护权值。
这样就可以通过用树状数组前缀和或是线段树区间查询解决转移问题。
总复杂度:$Theta(Nsqrt{M}log N)$
跑得还好说。
(有人说分块也行,大家加油咯!)
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define N 101101 #define LL long long //#include "debug.h" using namespace std; struct QUERY{ int l,r,a,b,t; LL ans1,ans2; }query[N*10]; int num[N]; int pre[N],n,qn,sqn; int inpart[N]; int tot[N],pretot[N]; inline int lowbit (int x){ return x&(-x); } inline int getpart(int x){ return (x-1)/sqn+1; } LL sum (int x){ LL s=0; for(int i=x;i;i-=lowbit(i)) s+=pre[i]; return s; } LL gettot (int x){ LL s=0; for(int i=x;i;i-=lowbit(i)) s+=pretot[i]; return s; } void chatot (int pos,int x){ while(pos<=n){ pretot[pos]+=x; pos+=lowbit(pos); } } void change (int pos,int x){ if(tot[pos]==1&&x==-1) chatot(pos,-1); else if(tot[pos]==0&&x== 1) chatot(pos,1); tot[pos]+=x; while(pos<=n){ pre[pos]+=x; pos+=lowbit(pos); } } //bool CMP (const QUERY &a,const QUERY &b){ // if(inpart[a.l]==inpart[b.r]) // return a.r<b.r; // return a.l<a.l; //} bool CMP(const QUERY &x,const QUERY &y){ return inpart[x.l]<inpart[y.l]||(inpart[x.l]==inpart[y.l]&&(inpart[x.l]&1?x.r<y.r:x.r>y.r)); } bool Cmp (const QUERY &a,const QUERY &b){ return a.t<b.t; } int main (){ int a,b,c,d; scanf("%d%d",&n,&qn);sqn=sqrt(1ll*n*n/qn)+1; for(int i=1;i<=n;i++){ scanf("%d",num+i); inpart[i]=getpart(i); } for(int i=1;i<=qn;i++){ scanf("%d%d%d%d",&a,&b,&c,&d); query[i].l=a,query[i].r=b,query[i].t=i; query[i].a=c,query[i].b=d; } sort(query+1,query+qn+1,CMP); //for(int i=1;i<=qn;i++)cout<<query[i].l<<" "<<query[i].r<<endl; int l=1,r=1,ql,qr; change(num[1],1); for(int i=1;i<=qn;i++){//pour(tot,1,n,3,"Tot"); ql=query[i].l,qr=query[i].r, a =query[i].a,b =query[i].b; while(l<ql)change(num[l] ,-1),l++; while(l>ql)change(num[l-1], 1),l--; while(r<qr)change(num[r+1], 1),r++; while(r>qr)change(num[r] ,-1),r--; query[i].ans1=sum(b)-sum(a-1); query[i].ans2=gettot(b)-gettot(a-1); } sort(query+1,query+qn+1,Cmp); for(int i=1;i<=qn;i++) printf("%lld %lld ",query[i].ans1,query[i].ans2); return 0; }