天使玩偶:CDQ分治&KD-tree

天使玩偶:CDQ分治&KD-tree

这道好(du)题(liu)还是很不错的 挺锻炼代码能力和不断优化 卡常的能力的。

对于 每次询问 我都可以将其分出方向 然后 写 也就是针对于4个方向 左下 左上 右下 右上

这样的话 就成功转换了问题 求4次 三维偏序即可 水题啊。

然后 打完代码 就提交 T飞了

天使玩偶:CDQ分治&KD-tree
//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
#define x(i) t[i].x
#define y(i) t[i].y
#define k(i) t[i].k
#define t(i) t[i].t
#define id(i) t[i].id
using namespace std;
char buf[1<<15],*ft,*fs;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?x=-x,putchar('-'):0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar(10);return;
}
const int MAXN=300002,maxn=1000001;
int n,m,tot,num,cnt;
struct wy
{
    int x,y;
    int t,k;
    int id;
}t[MAXN<<1],tmp[MAXN<<1];
int ans[MAXN],c[maxn+10];//树状数组维护前缀最大值!
//注意时间戳也得排序 我的思维漏洞!
int cmp(wy x,wy y)//左下
{
    if(x.x==y.x&&x.y==x.y)return x.t<y.t;
    return x.x==y.x?x.y<y.y:x.x<y.x;
}
int cmp1(wy x,wy y)//左上
{
    if(x.x==y.x&&x.y==x.y)return x.t<y.t;
    return x.x==y.x?x.y>y.y:x.x<y.x;
}
int cmp2(wy x,wy y)//右下
{
    if(x.x==y.x&&x.y==x.y)return x.t<y.t;
    return x.x==y.x?x.y<y.y:x.x>y.x;
}
int cmp3(wy x,wy y)//右上
{
    if(x.x==y.x&&x.y==x.y)return x.t<y.t;
    return x.x==y.x?x.y>y.y:x.x>y.x;
}
inline int max(int x,int y){return x>y?x:y;}
void add(int x,int y)
{
    if(y==INF){for(;x<=maxn;x+=x&(-x))c[x]=-INF;return;}
    for(;x<=maxn;x+=x&(-x))c[x]=max(c[x],y);
    return;
}
inline int ask(int x)
{
    int sum=-INF;
    for(;x;x-=x&(-x))sum=max(sum,c[x]);
    return sum;
}
void CDQ(int l,int r,int p)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(l,mid,p);
    CDQ(mid+1,r,p);
    int i=l,j=mid+1;
    if(p==0)
    {
        for(int k=l;k<=r;++k)
        {
            if(j>r||(i<=mid&&t(i)<=t(j)))
            {
                if(k(i)!=1)add(y(i),x(i)+y(i));
                tmp[k]=t[i];
                ++i;
            }
            else
            {
                if(k(j)==0){tmp[k]=t[j];++j;continue;}
                //if(id(j)==1)cout<<x(j)<<' '<<y(j)<<endl;
                int maxx=ask(y(j));
                if(maxx==-INF){tmp[k]=t[j];++j;continue;}
                ans[id(j)]=min(ans[id(j)],y(j)+x(j)-maxx);
                tmp[k]=t[j];
                ++j;
            }
        }
        for(int k=l;k<=r;k++)
        {
            if(k<=mid&&k(k)!=1)add(y(k),INF);
            t[k]=tmp[k];
        }
    }
    else
    {
        for(int k=l;k<=r;++k)
        {
            if(j>r||(i<=mid&&t(i)<=t(j)))
            {
                if(k(i)!=1)add(maxn-y(i),x(i)-y(i));
                tmp[k]=t[i];
                ++i;
            }
            else
            {
                if(k(j)==0){tmp[k]=t[j];++j;continue;}
                int maxx=ask(maxn-y(j));
                if(maxx==-INF){tmp[k]=t[j];++j;continue;}
                //cout<<maxx<<endl;
                ans[id(j)]=min(ans[id(j)],x(j)-y(j)-maxx);
                tmp[k]=t[j];
                ++j;
            }
        }
        for(int k=l;k<=r;k++)
            {
                if(k<=mid&&k(k)!=1)add(maxn-y(k),INF);
                t[k]=tmp[k];
            }
    }
    return;
}
void CDQ1(int l,int r,int p)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ1(l,mid,p);
    CDQ1(mid+1,r,p);
    int i=l,j=mid+1;
    if(p==0)
    {
        for(int k=l;k<=r;++k)
        {
            if(j>r||(i<=mid&&t(i)<=t(j)))
            {
                if(k(i)!=1)add(y(i),y(i)-x(i));
                tmp[k]=t[i];
                ++i;
            }
            else
            {
                if(k(j)==0){tmp[k]=t[j];++j;continue;}
                //if(id(j)==1)cout<<x(j)<<' '<<y(j)<<endl;
                int maxx=ask(y(j));
                if(maxx==-INF){tmp[k]=t[j];++j;continue;}
                ans[id(j)]=min(ans[id(j)],y(j)-x(j)-maxx);
                tmp[k]=t[j];
                ++j;
            }
        }
        for(int k=l;k<=r;k++)
        {
            if(k<=mid&&k(k)!=1)add(y(k),INF);
            t[k]=tmp[k];
        }
    }
    else
    {
        for(int k=l;k<=r;++k)
        {
            if(j>r||(i<=mid&&t(i)<=t(j)))
            {
                if(k(i)!=1)add(maxn-y(i),-x(i)-y(i));//此处不要想当然的写
                tmp[k]=t[i];
                ++i;
            }
            else
            {
                if(k(j)==0){tmp[k]=t[j];++j;continue;}
                int maxx=ask(maxn-y(j));
                if(maxx==-INF){tmp[k]=t[j];++j;continue;}
                ans[id(j)]=min(ans[id(j)],-maxx-x(j)-y(j));//变形要正确啊!
                tmp[k]=t[j];
                ++j;
            }
        }
        for(int k=l;k<=r;k++)
            {
                if(k<=mid&&k(k)!=1)add(maxn-y(k),INF);
                t[k]=tmp[k];
            }
    }
    return;
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    memset(ans,10,sizeof(ans));
    n=read();m=read();num=n;
    for(int i=1;i<=n;i++)x(i)=read(),y(i)=read();
    for(int i=1;i<=m;i++)
    {
        int p=read();
        x(++num)=read();y(num)=read();
        if(p==2)k(num)=1,id(num)=++cnt;
        t(num)=++tot;
    }
    //for(int i=1;i<=num;i++)cout<<x(i)<<' '<<y(i)<<' '<<t(i)<<' '<<k(i)<<endl;
    for(int i=1;i<=maxn;i++)c[i]=-INF;
    sort(t+1,t+1+num,cmp);//左下
    CDQ(1,num,0);//左下 
    //for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
    for(int i=1;i<=maxn;i++)c[i]=-INF;
    sort(t+1,t+1+num,cmp1);//左上
    CDQ(1,num,1);//左上 
    //for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
    for(int i=1;i<=maxn;i++)c[i]=-INF;
    sort(t+1,t+1+num,cmp2);//右下
    CDQ1(1,num,0);//右下 
    //for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
    for(int i=1;i<=maxn;i++)c[i]=-INF;
    sort(t+1,t+1+num,cmp3);//右上
    CDQ1(1,num,1);//右上
    //for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
    for(int i=1;i<=cnt;i++)put(ans[i]);
    return 0;
}
View Code

都别管我我要写4次CDQ 我要sort 4次然后不出意料只拿到50分。

原因是sort的次数多了 常数是别人的近乎4倍 (有的人一次sort都不做 按照时间直接排)

然后脑残的写了这么段代码 一直T 照着书上的 标程改了一下是一种比较简洁的做法。

但是其CDQ的每层都需要sort 一下比较浪费时间但是还是能拿到60分的 开O2 能A呢。

然后学了一下书上的做法 发现真心简单 代码复杂度 时间复杂度 空间复杂度包括常数都远远低于我

天使玩偶:CDQ分治&KD-tree
// luogu-judger-enable-o2
//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
#define x(i) t[i].x
#define y(i) t[i].y
#define t(i) t[i].t
#define id(i) t[i].id
using namespace std;
char buf[1<<15],*ft,*fs;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?x=-x,putchar('-'):0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar(10);return;
}
const int MAXN=600002,maxn=1000010;
int n,m,cnt,num,tot,h;
int ans[MAXN],c[maxn],maxx;
struct wy
{
    int x,y;
    int t;
    int id;
    friend int operator <(const wy &a,const wy &b)
    {
        return a.x==b.x?a.y<b.y:a.x<b.x;//此时排序不用时间戳(经过实验
        //考虑一次CDQ 分治 时间复杂度 ≈(n+m)*log(n+m)^3左右
    }
}t[MAXN],tmp[MAXN];
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x>y?y:x;}
inline void add(int x,int y){for(;x<=maxx;x+=x&(-x))c[x]=max(c[x],y);}
inline int abs(int x){return x<0?-x:x;}
inline int ask(int x)
{
    int maxx1=-INF;
    for(;x;x-=x&(-x))maxx1=max(maxx1,c[x]);
    return maxx1;
}
inline void remove1(int x)
{
    for(;x<=maxx;x+=x&(-x))c[x]=-INF;
    return;
}
void calculate(int st,int en,int w,int kx,int ky)//属性
{
    for(int i=st;i!=en;i+=w)
    {
        int yy=(ky==1?tmp[i].y:maxx-tmp[i].y);
        int sum=kx*tmp[i].x+ky*tmp[i].y;
        if(tmp[i].t==1)add(yy,sum);
        else 
        {
            int an=ask(yy);
            if(an==-INF)continue;
            ans[tmp[i].id]=min(ans[tmp[i].id],sum-an);
        }
        //if(tmp[i].id==2)cout<<sum<<' '<<ask(yy)<<endl;
    }
    for(int i=st;i!=en;i+=w)if(tmp[i].t==1)remove1(ky==1?tmp[i].y:maxx-tmp[i].y);
}
void CDQ(int l,int r)
{
    int mid=(l+r)>>1;
    if(l<mid)CDQ(l,mid);
    if(mid+1<r)CDQ(mid+1,r);
    h=0;
    for(int i=l;i<=r;i++)if((i<=mid&&t[i].t==1)||(i>mid&&t[i].t==2))tmp[++h]=t[i];
    sort(tmp+1,tmp+1+h);
    calculate(1,h+1,1,1,1);//左下
    calculate(1,h+1,1,1,-1);//左上
    calculate(h,0,-1,-1,1);//右下
    calculate(h,0,-1,-1,-1);//右上
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read();m=read();num=n;
    for(int i=1;i<=n;++i)
    {
        x(i)=read();
        y(i)=read();
        t(i)=1;
        maxx=max(maxx,y(i));
    }
    for(int i=1;i<=m;++i)
    {
        t(++num)=read();
        x(num)=read();
        y(num)=read();
        id(num)=t(num)==2?++cnt:0;
        maxx=max(maxx,y(num));
    }
    //put(cnt);
    ++maxx;//巧妙一招
    for(int i=1;i<=maxx;++i)c[i]=-INF;
    memset(ans,10,sizeof(ans));
    CDQ(1,num);
    for(int i=1;i<=cnt;i++)put(ans[i]);
    return 0;
}
View Code

比较难受 因为这浪费了我很多的时间 然后最后还是T 开了O2 死T一个点(原因是 我是个NC啊。。。

开O2 过了不算什么我要不开O2 过

点开题解发现我的第一个代码的思想及其接近其 题解中跑的最快的。

所以发现可以不用sort 于是顺利不开O2 第一个代码顺利得到80分。

发现可以加一个优化是 能不加到树状数组里就不加 到需要的时候再加 还有一些卡常的东西

最终不开O2 直接A (一直T的那个点是因为下标为0 树状数组原地GG了。。。)

天使玩偶:CDQ分治&KD-tree
//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
#define x(i) t[i].x
#define y(i) t[i].y
#define k(i) t[i].k
#define t(i) t[i].t
#define id(i) t[i].id
#define R register
using namespace std;
//char buf[1<<15],*ft,*fs;
//inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(int x)
{
    x<0?x=-x,putchar('-'):0;
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    num==0?putchar('0'):0;
    while(num)putchar(ch[num--]);
    putchar(10);return;
}
const int maxn=1000010;
int n,m,tot,num,cnt,maxx1,maxx2;
struct wy
{
    int x,y;
    int k,id;
    inline bool operator <(const wy &w)const
    {
        return x<w.x;
    }
}t[maxn],tmp[maxn],a[maxn];
int ans[maxn],c[maxn+10];
inline int max(int x,int y){return x>y?x:y;}
inline void add(int x,int y)
{
    
    for(;x<=maxx1;x+=x&(-x))
    {
        if(c[x]>y)break;
        else c[x]=y;
    }
    return;
}
inline void remove1(int x)
{
    for(;x<=maxx1;x+=x&(-x))if(c[x])c[x]=0;
    return;
}
inline int ask(int x)
{
    int sum=-INF;
    for(;x;x-=x&(-x))sum=max(sum,c[x]);
    return sum;
}
inline void CDQ(int l,int r)
{
    int mid=(l+r)>>1;
    if(l<mid)CDQ(l,mid);
    if(r>mid+1)CDQ(mid+1,r);
    R int i=l,j=mid+1;
    for(j=mid+1;j<=r;j++)
    {
        if(k(j)==1)
        {    
            for(;i<=mid&&x(i)<=x(j);i++)
                if(k(i)==0)add(y(i),x(i)+y(i));
            int maxx=ask(y(j));
            maxx==0?0:ans[id(j)]=min(ans[id(j)],y(j)+x(j)-maxx);
            
        }
    }
    for(j=l;j<i;j++)if(k(j)==0)remove1(y(j));
    i=l,j=mid+1;
    for(R int k=l;k<=r;++k)
    {
        if(j>r||(i<=mid&&t[i]<t[j]))tmp[k]=t[i],++i;
        else tmp[k]=t[j],++j;
    }
    for(R int k=l;k<=r;++k)t[k]=tmp[k];
    return;
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read();m=read();num=n;
    for(R int i=1;i<=n;++i){x(i)=read()+1,y(i)=read()+1;maxx1=max(maxx1,y(i));maxx2=max(maxx2,x(i));}
    for(R int i=1;i<=m;++i)
    {
        int p=read();
        x(++num)=read()+1;y(num)=read()+1;
        if(p==2)k(num)=1,id(num)=++cnt;
        maxx1=max(maxx1,y(num));
        maxx2=max(maxx2,x(num));
    }
    ++maxx1;++maxx2;
    for(R int i=1;i<=cnt;++i)ans[i]=INF;
    for(R int i=1;i<=num;++i)a[i]=t[i];
    CDQ(1,num);//左下 
    for(R int i=1;i<=num;++i)t[i]=a[i],y(i)=maxx1-y(i);
    CDQ(1,num);//左上 
    for(R int i=1;i<=num;++i)t[i]=a[i],x(i)=maxx2-x(i);
    CDQ(1,num);//右下 
    for(R int i=1;i<=num;++i)t[i]=a[i],x(i)=maxx2-x(i),y(i)=maxx1-y(i);
    CDQ(1,num);//右上
    for(R int i=1;i<=cnt;++i)put(ans[i]);
    return 0;
}
View Code

当然 为了能够完美的A掉这道题 我决定去学一个更强的东西 KD-tree.

然后在抄了20min代码后觉定放弃 尧神说没多大卵用。。。

所以这篇博文到此啦,关键是不断优化的思想 少开O2

上一篇:统计学习方法——KNN


下一篇:[bzoj2648]SJY摆棋子(带插入kd-tree)