[Ahoi2013]作业 解题报告

很久以前用奇葩做法搞的题。。现在补个解题报告。

传统做法:
考虑莫队,就需要我们设计一个 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,nexti1) )+1,单点查询。
那么至此我们就可以上k-d树了!注意这样查询的时间复杂度必然是 mlogm ,比较慢的地方在于矩形+1,而这比较慢的地方正好只有n个,所以就完美匹配上了n与m的关系。
但是,如果我们反过来想,用全部的答案减去不可能出现的,就是在 ((i+1,0)(,nexti1)) +1。这样就是一个前缀矩形,应该会跑的快点吧。。
但是!我们是理论计算机科学家,我们要追求最科学的做法。(其实是我年轻的时候并不会k-d树。)
注意到这样的话点并没有太大规律。。所以我们反过来看,把 (i+1,nexti1) 看成点,每次查询 (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]);
}

总结:
①补集转换往往会有奇效!

你可能感兴趣的:(分治,分块,主席树,莫队,k-d数)