天使玩偶:CDQ分治

时间:2023-03-09 04:56:36
天使玩偶:CDQ分治

天使玩偶:CDQ分治

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

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

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

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

//#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[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(int x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
const int MAXN=,maxn=;
int n,m,tot,num,cnt;
struct wy
{
int x,y;
int t,k;
int id;
}t[MAXN<<],tmp[MAXN<<];
int ans[MAXN],c[maxn+];//树状数组维护前缀最大值!
//注意时间戳也得排序 我的思维漏洞!
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)>>;
CDQ(l,mid,p);
CDQ(mid+,r,p);
int i=l,j=mid+;
if(p==)
{
for(int k=l;k<=r;++k)
{
if(j>r||(i<=mid&&t(i)<=t(j)))
{
if(k(i)!=)add(y(i),x(i)+y(i));
tmp[k]=t[i];
++i;
}
else
{
if(k(j)==){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)!=)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)!=)add(maxn-y(i),x(i)-y(i));
tmp[k]=t[i];
++i;
}
else
{
if(k(j)==){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)!=)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)>>;
CDQ1(l,mid,p);
CDQ1(mid+,r,p);
int i=l,j=mid+;
if(p==)
{
for(int k=l;k<=r;++k)
{
if(j>r||(i<=mid&&t(i)<=t(j)))
{
if(k(i)!=)add(y(i),y(i)-x(i));
tmp[k]=t[i];
++i;
}
else
{
if(k(j)==){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)!=)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)!=)add(maxn-y(i),-x(i)-y(i));//此处不要想当然的写
tmp[k]=t[i];
++i;
}
else
{
if(k(j)==){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)!=)add(maxn-y(k),INF);
t[k]=tmp[k];
}
}
return;
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
memset(ans,,sizeof(ans));
n=read();m=read();num=n;
for(int i=;i<=n;i++)x(i)=read(),y(i)=read();
for(int i=;i<=m;i++)
{
int p=read();
x(++num)=read();y(num)=read();
if(p==)k(num)=,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=;i<=maxn;i++)c[i]=-INF;
sort(t+,t++num,cmp);//左下
CDQ(,num,);//左下
//for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
for(int i=;i<=maxn;i++)c[i]=-INF;
sort(t+,t++num,cmp1);//左上
CDQ(,num,);//左上
//for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
for(int i=;i<=maxn;i++)c[i]=-INF;
sort(t+,t++num,cmp2);//右下
CDQ1(,num,);//右下
//for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
for(int i=;i<=maxn;i++)c[i]=-INF;
sort(t+,t++num,cmp3);//右上
CDQ1(,num,);//右上
//for(int i=1;i<=cnt;i++)cout<<ans[i]<<endl;
for(int i=;i<=cnt;i++)put(ans[i]);
return ;
}

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

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

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

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

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

// 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[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(int x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
const int MAXN=,maxn=;
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<?-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==?tmp[i].y:maxx-tmp[i].y);
int sum=kx*tmp[i].x+ky*tmp[i].y;
if(tmp[i].t==)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==)remove1(ky==?tmp[i].y:maxx-tmp[i].y);
}
void CDQ(int l,int r)
{
int mid=(l+r)>>;
if(l<mid)CDQ(l,mid);
if(mid+<r)CDQ(mid+,r);
h=;
for(int i=l;i<=r;i++)if((i<=mid&&t[i].t==)||(i>mid&&t[i].t==))tmp[++h]=t[i];
sort(tmp+,tmp++h);
calculate(,h+,,,);//左下
calculate(,h+,,,-);//左上
calculate(h,,-,-,);//右下
calculate(h,,-,-,-);//右上
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
n=read();m=read();num=n;
for(int i=;i<=n;++i)
{
x(i)=read();
y(i)=read();
t(i)=;
maxx=max(maxx,y(i));
}
for(int i=;i<=m;++i)
{
t(++num)=read();
x(num)=read();
y(num)=read();
id(num)=t(num)==?++cnt:;
maxx=max(maxx,y(num));
}
//put(cnt);
++maxx;//巧妙一招
for(int i=;i<=maxx;++i)c[i]=-INF;
memset(ans,,sizeof(ans));
CDQ(,num);
for(int i=;i<=cnt;i++)put(ans[i]);
return ;
}

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

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

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

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

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

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

//#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=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(int x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
const int maxn=;
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+];
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]=;
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)>>;
if(l<mid)CDQ(l,mid);
if(r>mid+)CDQ(mid+,r);
R int i=l,j=mid+;
for(j=mid+;j<=r;j++)
{
if(k(j)==)
{
for(;i<=mid&&x(i)<=x(j);i++)
if(k(i)==)add(y(i),x(i)+y(i));
int maxx=ask(y(j));
maxx==?:ans[id(j)]=min(ans[id(j)],y(j)+x(j)-maxx); }
}
for(j=l;j<i;j++)if(k(j)==)remove1(y(j));
i=l,j=mid+;
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=;i<=n;++i){x(i)=read()+,y(i)=read()+;maxx1=max(maxx1,y(i));maxx2=max(maxx2,x(i));}
for(R int i=;i<=m;++i)
{
int p=read();
x(++num)=read()+;y(num)=read()+;
if(p==)k(num)=,id(num)=++cnt;
maxx1=max(maxx1,y(num));
maxx2=max(maxx2,x(num));
}
++maxx1;++maxx2;
for(R int i=;i<=cnt;++i)ans[i]=INF;
for(R int i=;i<=num;++i)a[i]=t[i];
CDQ(,num);//左下
for(R int i=;i<=num;++i)t[i]=a[i],y(i)=maxx1-y(i);
CDQ(,num);//左上
for(R int i=;i<=num;++i)t[i]=a[i],x(i)=maxx2-x(i);
CDQ(,num);//右下
for(R int i=;i<=num;++i)t[i]=a[i],x(i)=maxx2-x(i),y(i)=maxx1-y(i);
CDQ(,num);//右上
for(R int i=;i<=cnt;++i)put(ans[i]);
return ;
}

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

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

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