BZOJ4066 - 简单题

原题链接

Description

对于一个n×n(n5×105)的矩阵进行m(m2×105)次操作:
1. 将格子(x,y)里的数字加上A(A>0)
2. 求矩形(x1,y1)(x2,y2)中的数字和。
强制在线,空间限制20MB。

Solution

k-d树。
对于操作1,将(x,y)插入树中,当树的大小达到一定程度就重建整棵树。
对于操作2,做法类似于线段树,如果查询矩形完全包含目前子树所表示的矩形,就返回这个子树内的和;否则就递归的向左右子树内查询。

并不知道k-d树的时间复杂度如何算。

Code

这个代码目前TLE!明天问问Icefox大佬
噫!我过了

//简单题
#include <cstdio>
#include <algorithm>
using namespace std;
int pre;
inline char gc()
{
    static char now[1<<16],*S,*T;
    if(S==T) {T=(S=now)+fread(now,1,1<<16,stdin); if(S==T) return EOF;}
    return *S++;
}
inline int read()
{
    int x=0; char ch=gc();
    while(ch<'0'||'9'<ch) ch=gc();
    while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x^pre;
}
int const N=2e5+10;
int const INF=0x7FFFFFFF;
int n;
#define chL ch[p][0]
#define chR ch[p][1]
int D,ndCnt,rt,ch[N][2],sum[N];
struct point
{
    int c[2],v;
    point(int x=0,int y=0,int v0=0){c[0]=x,c[1]=y,v=v0;}
}pt[N];
struct zone
{
    int c1[2],c2[2];
    zone(int x1=0,int y1=0,int x2=0,int y2=0)
    {
        c1[0]=x1,c1[1]=y1;
        c2[0]=x2,c2[1]=y2;
    }
}zn[N];
bool cmpPt(point x,point y) {return x.c[D]<y.c[D];}
void create(int p,point A)
{
    pt[p]=A;
    for(int k=0;k<2;k++) zn[p].c1[k]=zn[p].c2[k]=A.c[k];
    chL=chR=0; sum[p]=A.v;
}
void update(int p)
{
    for(int k=0;k<2;k++)
    {
        zn[p].c1[k]=min(pt[p].c[k],min(zn[chL].c1[k],zn[chR].c1[k]));
        zn[p].c2[k]=max(pt[p].c[k],max(zn[chL].c2[k],zn[chR].c2[k]));
    }
    sum[p]=pt[p].v+sum[chL]+sum[chR];
}
inline bool equal(point x,point y)
{
    for(int k=0;k<2;k++) if(x.c[k]!=y.c[k]) return false;
    return true;
}
void build(int &p,int L,int R,int k0)
{
    int mid=L+R>>1; D=k0;
    nth_element(pt+L,pt+mid,pt+R+1,cmpPt);
    create(p=mid,pt[mid]);
    if(L<mid) build(chL,L,mid-1,k0^1);
    if(mid<R) build(chR,mid+1,R,k0^1);
    update(p);
}
void ins(int &p,point A,int k0)
{
    if(p==0) {create(p=++ndCnt,A); return;}
    if(equal(A,pt[p])) {pt[p].v+=A.v,sum[p]+=A.v; return;}
    ins(ch[p][A.c[k0]>pt[p].c[k0]],A,k0^1);
    update(p);
}
inline bool in(zone z,zone z0)
{
    for(int k=0;k<2;k++) if(z0.c1[k]<z.c1[k]||z.c2[k]<z0.c2[k]) return false;
    return true;
}
inline bool in(zone z,point A)
{
    for(int k=0;k<2;k++) if(A.c[k]<z.c1[k]||z.c2[k]<A.c[k]) return false;
    return true;
}
inline bool out(zone z,zone z0)
{
    for(int k=0;k<2;k++)
    {
        bool outL=z0.c1[k]<z.c1[k]&&z0.c2[k]<z.c1[k];
        bool outR=z0.c1[k]>z.c2[k]&&z0.c2[k]>z.c2[k];
        if(outL||outR) return true;
    }
    return false;
}
int query(int p,zone z)
{
    if(in(z,zn[p])) return sum[p];
    int res=0;
    if(in(z,pt[p])) res+=pt[p].v;
    if(!out(z,zn[chL])) res+=query(chL,z);
    if(!out(z,zn[chR])) res+=query(chR,z);
    return res;
}
int main()
{
    pre=0; n=read();
    for(int k=0;k<2;k++) zn[0].c1[k]=INF,zn[0].c2[k]=-INF;
    while(true)
    {
        int opt=read()^pre;
        if(opt==1)
        {
            int x=read(),y=read(),k=read();
            ins(rt,point(x,y,k),0);
            if(ndCnt%10000==0) rt=0,build(rt,1,ndCnt,0);
        }
        else if(opt==2)
        {
            int x1=read(),y1=read(),x2=read(),y2=read();
            printf("%d\n",pre=query(rt,zone(x1,y1,x2,y2)));
        }
        else break;
    }
    return 0;
}

P.S.

到底什么锅啊!跟Icefox的代码对拍十组都一样啊!
建树/插入节点的时候,参数要单独传一个k0,表示当前这层以第k0维作为划分标准,而不能用全局变量。那个全局变量D只是用于做nth_element()的…
对拍正确可能是因为只有两维+数据分散,有时候k0错了也能跳到正确的子树上

posted @ 2018-01-12 23:32  VisJiao  阅读(170)  评论(0编辑  收藏  举报