[bzoj4066]简单题 解题报告

时间:2022-12-17 17:07:17

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

大爷的做法:听说是把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>
//Read
char * 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);
        }
}

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