[bzoj4066]简单题 解题报告

来源:互联网 发布:杰奇cms破解版 编辑:程序博客网 时间:2024/05/22 13:46

也是很久以前奇葩做法搞的题,补个解题报告。

大爷的做法:听说是把k-d树写成替罪的或是每n重建。
傻逼的做法:
我们是理论计算机科学家(当时我弱不会k-d树。。),所以我们需要思考科学的做法。
分块!
考虑将横坐标分成n份,纵坐标分成n份,这样会有n个块。内存首先比较科学了。我们显然需要每插入n个元素重建。我们对每一行维护前缀和,维护的代价是O(n)的,查询完全覆盖的块的和也是O(n)的。然后我们希望询问任意一个矩阵,所有被不完全覆盖的块中元素的个数和是O(n)的。
我们可以这样分块,我们按x从小到大扫描,如果x=i的点的个数加到i-1所在的块里大于n了,就为i新开一块。对于y同理。这样的话显然单个坐标块的个数是O(2n)的。这样的话如果一个块的横坐标长度长度大于1,那么显然它的大小小于n;而如果一个块的横坐标长度等于1,那么它的纵坐标至多跨过这一行的n个点,所以每个块中点数都是不超过n的。
而查询一个矩形的时候,考虑它的一个边界x=a,如果它不完全覆盖的块的y长度大于1,那么显然这条边界上的所有块的总点数不会超过n,而如果它等于1,那么就与不完全覆盖矛盾了。所以矩形边界上我们只需暴力最多4n个点。
于是我们就得到了时间复杂度O((n+m)n)的科学做法。

最后口胡一下更加科学的搞法!
最近做了带插入区间第k大,对块链基本有了一些了解。这题如果用块链搞的话可以像bzoj3720我的傻逼做法一样,对x和y分别维护一个块链,然后在x中维护y的每个块的前缀和。这样时间复杂度是O(mm),空间复杂度是4M+MB2(每块大小为B)的。
显然比上面那种做法会好写很多+快很多。。但是我懒得写了。。

然后非常喜闻乐见。。我写了9K,别人都只写了3、4K。。

#include<cstdio>#include<cstring>#include<iostream>using namespace std;#include<algorithm>//Readchar * cp=(char *)malloc(5672730);inline void in(int &x){    while(*cp<'0'||*cp>'9')++cp;    x=0;    while(*cp>='0'&&*cp<='9')x=x*10+(*cp++^'0');}struct PS{    int x,y,A;}point[200005];inline bool hcmp(const int & a,const int & b){    return point[a].x!=point[b].x?point[a].x<point[b].x:point[a].y<point[b].y;}inline bool scmp(const int & a,const int & b){    return point[a].y<point[b].y;}//块下链表,块的编号从1开始。 int heng[955],shu[955];int s[955][955];int ptr[955][955],next[200005],succ[200005];int ltot=1;//排序过的点,h代表以heng为第一关键字,s代表以shu为第一关键字。 int ah[200005],as[200005],bs[200005];//对付蛋疼的询问。 int x1,y1,x2,y2;inline int query(int bx,int by){    //cout<<"Query("<<bx<<","<<by<<")\n";    int ans=0;    //cout<<ptr[3][2]<<endl;    for(int i=ptr[bx][by];i;i=next[i]){        //cout<<succ[i]<<"("<<i<<"):";        if(x1<=point[succ[i]].x&&point[succ[i]].x<=x2&&y1<=point[succ[i]].y&&point[succ[i]].y<=y2){            ans+=point[succ[i]].A;            //cout<<"Get!";        }        //puts("");    }    //cout<<"Ans="<<ans<<endl;    return ans;}inline int cquery(int bx,int ly,int ry){    //cout<<"c("<<bx<<","<<"["<<ly<<","<<ry<<"])\n";    return ly!=ry?s[bx][ry-1]-s[bx][ly]+(y2==shu[ry]?s[bx][ry]-s[bx][ry-1]:query(bx,ry))+(y1==(ly>1?shu[ly-1]+1:1)?s[bx][ly]-s[bx][ly-1]:query(bx,ly)):(ly==(ly>1?shu[ly-1]+1:1)&&ry==shu[ry]?s[bx][ry]-s[bx][ly-1]:query(bx,ly));}inline int nquery(int bx,int ly,int ry){    int ans=0;    while(ly<=ry)ans+=query(bx,ly++);    return ans;}int main(){    fread(cp,1,5672730,stdin);    int n,i,opt,bx,by,last_ans=0,j,lx,rx,ly,ry;    int atot=0,btot=0;//a是有多少个科学的点,b是有多少个不科学的点。不科学的点在科学的点的后面。    int ptot=1;//p是有多少个点。     int tot;//归并排序用的。     int now=0;    int S=400;    in(n);    heng[0]=1,heng[1]=n;    shu[0]=1,shu[1]=n;    for(in(opt);opt!=3;in(opt))        if(opt==1){            in(point[ptot].x),in(point[ptot].y),in(point[ptot].A);            point[ptot].x^=last_ans,point[ptot].y^=last_ans,point[ptot].A^=last_ans;            bx=lower_bound(heng+1,heng+heng[0]+1,point[ptot].x)-heng;            by=lower_bound(shu+1,shu+shu[0]+1,point[ptot].y)-shu;            ah[atot+btot]=as[atot+btot]=ptot;            ++btot;            //cout<<ltot<<":"<<bx<<","<<by<<"->"<<ptr[bx][by]<<endl;            next[ltot]=ptr[bx][by],ptr[bx][by]=ltot,succ[ltot++]=ptot;            for(;by<=shu[0];++by)s[bx][by]+=point[ptot].A;            ++ptot;            if(btot>S){                //puts("----Rebuild-----");                //先清空。                ltot=1;                for(i=heng[0];i;--i){                    memset(s[i],0,sizeof(int)*(shu[0]+1));                    memset(ptr[i],0,sizeof(int)*(shu[0]+1));                }                heng[0]=shu[0]=0;                //归并                 tot=0;                sort(ah+atot,ah+atot+btot,hcmp);                for(i=j=0;i<atot&&j<btot;)bs[tot++]=hcmp(ah[i],ah[atot+j])?ah[i++]:ah[atot+j++];                while(i<atot)bs[tot++]=ah[i++];                while(j<btot)bs[tot++]=ah[atot+j++];                memcpy(ah,bs,sizeof(int)*tot);                tot=0;                sort(as+atot,as+atot+btot,scmp);                for(i=j=0;i<atot&&j<btot;)bs[tot++]=scmp(as[i],as[atot+j])?as[i++]:as[atot+j++];                while(i<atot)bs[tot++]=as[i++];                while(j<btot)bs[tot++]=as[atot+j++];                memcpy(as,bs,sizeof(int)*tot);                atot+=btot;                /*for(i=0;i<atot;++i)cout<<ah[i]<<" ";                cout<<endl;*/                //重新建块                 now=btot=0;                shu[0]=1;                for(i=0;i<atot;i=j+1){                    j=i;                    while(point[as[j+1]].y==point[as[j]].y)++j;                    if(now&&now+j-i+1>S){                        shu[shu[0]++]=point[as[i-1]].y;                        now=0;                    }                    if(j-i+1<S){                        now+=j-i+1;                        j=i-1;                        do{                            ++j;                            bs[as[j]]=shu[0];                        }while(point[as[j]].y==point[as[j+1]].y);                    }                    else{                        if(!shu[0]){                            shu[1]=point[as[i]].y-1;                            shu[0]=2;                        }                        else{                            if(point[as[i]].y!=1&&!(shu[0]>1&&shu[shu[0]-1]==point[as[i]].y-1))shu[shu[0]++]=point[as[i]].y-1;                            shu[shu[0]]=point[as[i]].y;                            j=i-1;                            do{                                ++j;                                bs[as[j]]=shu[0];                            }while(point[as[j]].y==point[as[j+1]].y);                            ++shu[0];                        }                        now=0;                    }                }                if(shu[shu[0]-1]==n)--shu[0];                else shu[shu[0]]=n;                now=0;                heng[0]=1;                for(i=0;i<atot;i=j+1){                    //cout<<"---"<<i<<"---\n";                    j=i;                    while(point[ah[j+1]].x==point[ah[j]].x)++j;                    if(now&&now+j-i+1>S){                        heng[heng[0]++]=point[ah[i-1]].x;                        now=0;                    }                    if(j-i+1<S){                        now+=j-i+1;                        j=i-1;                        do{                            ++j;                            next[ltot]=ptr[heng[0]][bs[ah[j]]],ptr[heng[0]][bs[ah[j]]]=ltot,succ[ltot++]=ah[j];                            s[heng[0]][bs[ah[j]]]+=point[ah[j]].A;                            //cout<<ah[j]<<":"<<heng[0]<<","<<bs[ah[j]]<<endl;                        }while(point[ah[j]].x==point[ah[j+1]].x);                    }                    else{                        if(!heng[0]){                            heng[1]=point[ah[i]].x-1;                            heng[0]=2;                        }                        else{                            if(point[ah[i]].x!=1&&!(heng[0]>1&&heng[heng[0]-1]==point[ah[i]].x-1))heng[heng[0]++]=point[ah[i]].x-1;                            heng[heng[0]]=point[ah[i]].x;                            j=i-1;                            do{                                ++j;                                next[ltot]=ptr[heng[0]][bs[ah[j]]],ptr[heng[0]][bs[ah[j]]]=ltot,succ[ltot++]=ah[j];                                s[heng[0]][bs[ah[j]]]+=point[ah[j]].A;                                //cout<<ah[j]<<":"<<heng[0]<<","<<bs[ah[j]]<<endl;                            }while(point[ah[j]].x==point[ah[j+1]].x);                            ++heng[0];                        }                        now=0;                    }                }                if(heng[heng[0]-1]==n)--heng[0];                else heng[heng[0]]=n;                /*for(i=1;i<=shu[0];++i)cout<<" "<<shu[i];                cout<<endl;                for(i=1;i<=heng[0];++i)cout<<" "<<heng[i];                cout<<endl;*/                for(i=heng[0];i;--i)                    for(j=1;j<=shu[0];++j){                        //cout<<"S("<<i<<","<<j<<")="<<s[i][j]<<endl;                        s[i][j]+=s[i][j-1];                    }            }        }        else{            in(x1),in(y1),in(x2),in(y2);            x1^=last_ans,y1^=last_ans,x2^=last_ans,y2^=last_ans;            //cout<<"["<<x1<<","<<x2<<"]*("<<y1<<","<<y2<<"]\n";            lx=lower_bound(heng+1,heng+heng[0]+1,x1)-heng,rx=lower_bound(heng+1,heng+heng[0]+1,x2)-heng;            ly=lower_bound(shu+1,shu+shu[0]+1,y1)-shu,ry=lower_bound(shu+1,shu+shu[0]+1,y2)-shu;            last_ans=0;            if(x2>=heng[lx]&&x1==(lx>1?heng[lx-1]:0)+1)last_ans+=cquery(lx,ly,ry);            else last_ans+=nquery(lx,ly,ry);            for(i=lx;++i<rx;)last_ans+=cquery(i,ly,ry);            if(rx!=lx)                if(x2==heng[rx])last_ans+=cquery(rx,ly,ry);                else last_ans+=nquery(rx,ly,ry);            printf("%d\n",last_ans);        }}

总结:
很久以前写的题。。现在并不知道该总结什么了。。感觉总是弄些巨难写代码巨长常数巨大的分块真是傻逼。

0 0
原创粉丝点击