很久以前用奇葩做法搞的题。。现在补个解题报告。
传统做法:
考虑莫队,就需要我们设计一个
O(1)
插入,
O(n√)
查询的数据结构,显然就需要对权值分块,记每个数出现次数和每个块两问的答案(或者第一问另写个主席树也可以,我比较懒)。莫队的块就分成
nm√
块。时间复杂度
O(mn√+nm−−√)
。非常好写,跑的飞快。
傻逼如何做这个题?
注意到我们有一个条件没有用到——为什么n比m小?这一定有其原因!(难道不是因为n和m一样大就mle了么。。)
第一问显然可以离线,然后把数按权值从小到大插入。然后就相当于是要支持单点+1,区间查询,显然bit最合适不过了。
对于第二问,首先我们把每个询问拆成两个,询问[l,r]中小于等于b的数值数量和小于等于a的数值数量,每个询问是
(l,r,b)
然后我们按b从小到大考虑。对i,设它上一个与它相同的数的坐标是
lasti
(如果没有就为0),它下一个与它相同的数的坐标是
nexti
(如果没有就为n+1),那么如果我们把每个询问看成点
(l,r)
的话,就相当于是有矩形(
(lasti+1,i)−(i,nexti−1)
)+1,单点查询。
那么至此我们就可以上k-d树了!注意这样查询的时间复杂度必然是
mlogm
,比较慢的地方在于矩形+1,而这比较慢的地方正好只有n个,所以就完美匹配上了n与m的关系。
但是,如果我们反过来想,用全部的答案减去不可能出现的,就是在
((i+1,0)−(∞,nexti−1))
+1。这样就是一个前缀矩形,应该会跑的快点吧。。
但是!我们是理论计算机科学家,我们要追求最科学的做法。(其实是我年轻的时候并不会k-d树。)
注意到这样的话点并没有太大规律。。所以我们反过来看,把
(i+1,nexti−1)
看成点,每次查询
(0,r)−(l,∞)
中有多少点。这样的话,n个点的横坐标是互不相同的,所以我们可以用cdq分治搞掉横坐标,然后用分块维护。分块,对于长为len的区间,按
len−−−√
的大小分块。这样插入点的代价是
O(∑∞i=0n2i−−√)=O(n√)
,而每次查询是
O(1)
的,要查询
O(logn)
次。所以总时间复杂度是
O(nn√+mlogn)
!所以cdq分治和分块在这里就完美结合了。。但是注意到我们需要在cdq分治的时候hash。。所以需要四个归并排序。。常数十分感人。想跑过k-d树应该是不可能的了。
代码(莫队):
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#include<algorithm>
int mb[100005],qb[100005],start[1000],end[1000];
int now[100005],shunum[1000],zhinum[1000];
int a[100005];
struct QS{
int l,r,a,b,i;
inline bool operator < (const QS &o)const{
return mb[l]!=mb[o.l]?l<o.l:r<o.r;
}
}q[1000005];
int ansshu[1000005],anszhi[1000005];
char * ptr=(char *)malloc(30000000);
inline void in(int &x){
x=0;
while(*ptr<'0'||*ptr>'9')++ptr;
while(*ptr>='0'&&*ptr<='9')x=x*10+(*ptr++^'0');
}
inline void out(int x){
if(!x)putchar('0');
else{
if(x>=10)out(x/10);
putchar('0'^x%10);
}
}
inline void update(int node,int delta){
zhinum[qb[node]]-=(bool)now[node];
now[node]+=delta,shunum[qb[node]]+=delta;
zhinum[qb[node]]+=(bool)now[node];
//if(qb[node]==4)cout<<"5!:"<<now[node]<<":"<<shunum[qb[node]]<<","<<zhinum[qb[node]]<<endl;
//cout<<node<<"->"<<now[node]<<endl;
}
int main(){
fread(ptr,1,30000000,stdin);
int n,m,i,k,j;
for(i=0,k=0;i<=100000;i+=200,++k)
for(j=i+200;j>i;--j)
mb[j]=k;
for(i=0,k=0;i<=100000;i+=318,++k){
for(j=min(i+318,100000);j>i;--j)qb[j]=k;
start[k]=i+1,end[k]=min(i+318,100000);
}
/*for(i=0,k=0;i<=100;i+=6,++k){
for(j=min(i+6,100);j>i;--j)qb[j]=k;
start[k]=i+1,end[k]=min(i+6,100);
}*/
in(n),in(m);
for(i=1;i<=n;++i)in(a[i]);
for(i=m;i--;)in(q[i].l),in(q[i].r),in(q[i].a),in(q[i].b),q[i].i=i;
sort(q,q+m);
int l=1,r=1;
now[a[1]]=shunum[qb[a[1]]]=zhinum[qb[a[1]]]=1;
for(i=0;i<m;++i){
if(q[i].a>n)continue;
/*cout<<"-----"<<q[i].l<<","<<q[i].r<<" "<<q[i].a<<","<<q[i].b<<"-----\n";
cout<<"["<<l<<","<<r<<"]\n";*/
q[i].b=min(q[i].b,n);
while(l<q[i].l)update(a[l++],-1);
while(l>q[i].l)update(a[--l],1);
while(r<q[i].r)update(a[++r],1);
while(r>q[i].r)update(a[r--],-1);
//cout<<qb[q[i].a]<<"->"<<qb[q[i].b]<<endl;
if(qb[q[i].a]!=qb[q[i].b]){
for(j=qb[q[i].a];++j<qb[q[i].b];)ansshu[q[i].i]+=shunum[j],anszhi[q[i].i]+=zhinum[j];
for(j=q[i].a;j<=end[qb[q[i].a]];++j)ansshu[q[i].i]+=now[j],anszhi[q[i].i]+=(bool)now[j];
for(j=start[qb[q[i].b]];j<=q[i].b;++j)ansshu[q[i].i]+=now[j],anszhi[q[i].i]+=(bool)now[j];
}
else for(j=q[i].a;j<=q[i].b;++j)ansshu[q[i].i]+=now[j],anszhi[q[i].i]+=(bool)now[j];
//cout<<"233:"<<now[5]<<" "<<shunum[qb[5]]<<endl;
}
for(i=m;i--;){
out(ansshu[i]);
putchar(' ');
out(anszhi[i]);
puts("");
}
}
代码(cdq分治+分块):
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdlib>
using namespace std;
char * cp=(char *)malloc(30000000);
inline void in(int &x){
while(*cp<'0'||*cp>'9')++cp;
x=0;
while(*cp>='0'&&*cp<='9')x=x*10+(*cp++^'0');
}
int a[100005];
int n;
struct QS{
int l,r,b,i;
}q[2000005];
//第一问
pair<int,int> sorted[100005];
int ans1[1000005],ans2[1000005];
int bit[100005];
bool cmp1(QS a,QS b){
return a.b>b.b;
}
bool cmp2(QS a,QS b){
return a.l!=b.l?a.l<b.l:a.r<b.r;
}
bool cmp3(int a,int b){
return q[a].r<q[b].r;
}
bool cmp4(int a,int b){
return q[a].b<q[b].b;
}
inline int query(int x){
int ans=0;
for(;x;x-=x&-x)ans+=bit[x];
return ans;
}
inline int query2(int x){
int ans=0;
for(;x<=n;x+=x&-x)ans+=bit[x];
return ans;
}
//第二问
int sqrt[100005];
int pos[100005];
int ct[100005],cy[100005],qt[2000005],qy[2000005],ch[100005],qh[2000005],tmp[2000005];
struct PS{
int l,r,b;
}point[100005];
int bsum[1000],wb[100005],sum[100005];
bool cmp5(int a,int b){
return point[a].r<point[b].r;
}
bool cmp6(int a,int b){
return point[a].b<point[b].b;
}
void binary(int pl,int pr,int ql,int qr){
if(ql>qr){
sort(cy+pl,cy+pr+1,cmp5);
sort(ct+pl,ct+pr+1,cmp6);
return;
}
int qm=ql-1;
while(qm<qr&&point[pl+pr>>1].l>=q[qm+1].l)++qm;
if(pl!=pr)binary(pl,pl+pr>>1,ql,qm),binary((pl+pr>>1)+1,pr,qm+1,qr);
else{
sort(qy+ql,qy+qr+1,cmp3);
sort(qt+ql,qt+qr+1,cmp4);
qm=ql-1;
}
int S=sqrt[pr-pl+1];
int i,j,k,htot=1,btot=1,pm=pl+pr>>1;
/*cout<<"----"<<pl<<","<<pr<<" "<<ql<<","<<qr<<"----\n";
for(i=pl;i<=pr;++i)cout<<cy[i]<<' ';
cout<<endl;
for(i=pl;i<=pr;++i)cout<<ct[i]<<' ';
cout<<endl;
for(i=ql;i<=qr;++i)cout<<qy[i]<<' ';
cout<<endl;
for(i=ql;i<=qr;++i)cout<<qt[i]<<' ';
cout<<endl;*/
//hash
for(i=pl,j=qm+1;i<=pm&&point[cy[i]].r<n;++i,++htot){
ch[cy[i]]=htot;
while(point[cy[i]].r==point[cy[i+1]].r)ch[cy[i++]]=htot;
for(;j<=qr&&q[qy[j]].r<=point[cy[i]].r;++j)qh[qy[j]]=htot;
}
while(i<=pm)ch[cy[i++]]=htot;
while(j<=qr)qh[qy[j++]]=htot;
++htot;
//分块
for(i=1,j=1;i<htot;i=j,++btot){
//cout<<"-"<<j<<' '<<(j<htot)<<" "<<(j-i!=S)<<endl;
for(;j<htot&&j-i!=S;++j){
//cout<<"+"<<j<<endl;
wb[j]=btot;
}
}
//查询
memset(bsum,0,sizeof(int)*btot);
memset(sum,0,sizeof(int)*htot);
for(i=pl,j=qm+1;j<=qr;)
if(i<=pm&&point[ct[i]].b<=q[qt[j]].b){
//cout<<"Add:"<<ct[i]<<endl;
for(k=ch[ct[i]];wb[k]==wb[ch[ct[i]]];--k)++sum[k];
for(k=wb[ch[ct[i]]];--k;)++bsum[k];
++i;
}
else{
if(q[qt[j]].l>=point[pm].l){
//cout<<(q[qt[j]].i>>1)<<" Get:";
if(q[qt[j]].i&1){
ans2[q[qt[j]].i>>1]-=sum[qh[qt[j]]]+bsum[wb[qh[qt[j]]]];
//putchar('-');
}
else ans2[q[qt[j]].i>>1]+=sum[qh[qt[j]]]+bsum[wb[qh[qt[j]]]];
//cout<<sum[qh[qt[j]]]+bsum[wb[qh[qt[j]]]]<<endl;
}
++j;
}
//归并
if(pl!=pr){
//-c
//--cy
for(i=pl,j=pm+1,k=pl;i<=pm||j<=pr;++k)
if(j>pr||i<=pm&&point[cy[i]].r<point[cy[j]].r)tmp[k]=cy[i++];
else tmp[k]=cy[j++];
for(k=pl;k<=pr;++k)cy[k]=tmp[k];
//--ct
for(i=pl,j=pm+1,k=pl;i<=pm||j<=pr;++k)
if(j>pr||i<=pm&&point[ct[i]].b<point[ct[j]].b)tmp[k]=ct[i++];
else tmp[k]=ct[j++];
for(k=pl;k<=pr;++k)ct[k]=tmp[k];
//-q
//--qy
for(i=ql,j=qm+1,k=ql;i<=qm||j<=qr;++k)
if(j>qr||i<=qm&&q[qy[i]].r<q[qy[j]].r)tmp[k]=qy[i++];
else tmp[k]=qy[j++];
for(k=ql;k<=qr;++k)qy[k]=tmp[k];
//--qt
for(i=ql,j=qm+1,k=ql;i<=qm||j<=qr;++k)
if(j>qr||i<=qm&&q[qt[i]].b<q[qt[j]].b)tmp[k]=qt[i++];
else tmp[k]=qt[j++];
for(k=ql;k<=qr;++k)qt[k]=tmp[k];
}
}
int main(){
fread(cp,1,30000000,stdin);
int i,m,k,j,qtot=0;
in(n),in(m);
for(i=1;i<=n;++i)in(a[i]),sorted[i]=make_pair(a[i],i);
for(i=m;i--;){
in(q[i<<1].l),in(q[i<<1].r),in(q[i<<1].b);
q[i<<1].b=max(q[i<<1].b,1)-1,q[i<<1].l=max(q[i<<1].l,1),q[i<<1].r=min(q[i<<1].r,n);
q[i<<1].i=i<<1;
q[i<<1|1]=q[i<<1];
in(q[i<<1|1].b);
q[i<<1|1].b=min(q[i<<1|1].b,n);
q[i<<1|1].i=i<<1|1;
}
//for(i=0;i!=m<<1;++i)printf("%d,%d,%d\n",q[i].l,q[i].r,q[i].b);
//第一问
sort(sorted+1,sorted+n+1);
sorted[n+1].first=-1;
sort(q,q+(m<<1),cmp1);
qtot=m<<1;
while(q[qtot-1].b==0)--qtot;
k=qtot-1;
for(;~k&&q[k].b<sorted[1].first;--k){
//cout<<"Query("<<q[k].i<<")["<<q[k].l<<","<<q[k].r<<"]->"<<query(q[k].r)-query(q[k].l-1)<<"\n";
if(q[k].i&1)ans1[q[k].i>>1]+=query(q[k].r)-query(q[k].l-1);
else ans1[q[k].i>>1]-=query(q[k].r)-query(q[k].l-1);
}
for(i=1;i<=n;++i){
//cout<<"---------\n";
//cout<<"Add:"<<sorted[i].second<<endl;
for(j=sorted[i].second;j<=n;j+=j&-j)++bit[j];
while(sorted[i].first==sorted[i+1].first){
//cout<<"Add:"<<sorted[i+1].second<<endl;
for(j=sorted[++i].second;j<=n;j+=j&-j)
++bit[j];
}
for(;~k&&(i==n||q[k].b<sorted[i+1].first);--k){
//cout<<"Query("<<q[k].i<<")["<<q[k].l<<","<<q[k].r<<"]->"<<query(q[k].r)-query(q[k].l-1)<<"\n";
if(q[k].i&1)ans1[q[k].i>>1]+=query(q[k].r)-query(q[k].l-1);
else ans1[q[k].i>>1]-=query(q[k].r)-query(q[k].l-1);
}
}
//for(i=m;i--;)printf("%d\n",ans1[i]);
//第二问
for(i=1;i*i<=n;++i)
for(j=i*i;j<min((i+1)*(i+1),n+1);++j)
sqrt[j]=i;
for(i=n;i;--i)pos[i]=n+1;
int ptot=0;
for(i=n;i;--i){
if(pos[a[i]]!=i+1)point[ptot++]=(PS){i+1,pos[a[i]]-1,a[i]};
pos[a[i]]=i;
}
memset(bit,0,sizeof(bit));
for(i=1,k=qtot-1;i<=n;++i){
//cout<<"Add:"<<pos[i]-1<<"\n";
if(pos[i]>1)
for(j=pos[i]-1;j;j-=j&-j)
++bit[j];
for(;~k&&q[k].b<=i;--k){
//cout<<"("<<(q[k].i>>1)<<")Get:";
if(q[k].i&1){
ans2[q[k].i>>1]+=q[k].b-query2(q[k].r);
//cout<<'+'<<q[k].b-query2(q[k].r);
}
else{
ans2[q[k].i>>1]-=q[k].b-query2(q[k].r);
//cout<<'-'<<q[k].b-query2(q[k].r);
}
//cout<<endl;
}
}
for(i=ptot>>1;i--;)swap(point[i],point[ptot-i-1]);
sort(q,q+qtot,cmp2);
for(i=ptot;i--;)cy[i]=ct[i]=i;
for(i=qtot;i--;)qy[i]=qt[i]=i;
/*cout<<"----point----\n";
for(i=0;i<ptot;++i)cout<<point[i].l<<","<<point[i].r<<" "<<point[i].b<<endl;
cout<<"---query----\n";
for(i=0;i<qtot;++i)cout<<q[i].l<<","<<q[i].r<<' '<<q[i].b<<' '<<(q[i].i>>1)<<endl;*/
binary(0,ptot-1,0,qtot-1);
//输出
for(i=m;i--;)printf("%d %d\n",ans1[i],ans2[i]);
}
总结:
①补集转换往往会有奇效!