[玄学分治 || 线段树] BZOJ 2675 Bomb & Tsin 1322 Bomb(李超)

分治 专栏收录该内容
16 篇文章 0 订阅

玄学分治做法 %%% http://www.cnblogs.com/ccz181078/p/5603283.html


三个点(x1,y1),(x2,y2),(x3,y3)的两两曼哈顿距离和为2(max(x1,x2,x3)+max(y1,y2,y3)-min(x1,x2,x3)-min(y1,y2,y3))

四项三个点 由抽屉原理 必至少有两项是同一个点的

那么最大值就是

max((x+y)max-xmin-ymin,xmax+ymax-(x+y)min,(x-y)max-xmin+ymax,xmax-ymin-(x-y)min)


最小值分治跟最近点对很相似

这里为了防卡 把xy交换了


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=100005;

struct Point{
  int x,y;
  void read(){
    ::read(x); ::read(y);
  }
  bool operator < (const Point &B)const {
    return x==B.x?y<B.y:x<B.x;
  }
}P[N],tmp[N];
int pnt=0;

bool cmp(Point A,Point B){
  return A.y==B.y?A.x<B.x:A.y<B.y;
}

inline int Calc(Point A,Point B,Point C){
  return max(A.x,max(B.x,C.x))-min(A.x,min(B.x,C.x))+max(A.y,max(B.y,C.y))-min(A.y,min(B.y,C.y));
}

int n;
int Ans=1<<30;

inline void Solve(int l,int r){
  if (r-l<15){
    for (int i=l;i<=r;i++) for (int j=i+1;j<=r;j++) for (int k=j+1;k<=r;k++) Ans=min(Ans,Calc(P[i],P[j],P[k]));
    return;
  }
  int mid=(l+r)>>1;
  Solve(l,mid); Solve(mid,r);
  pnt=0;
  for (int i=mid;i<=r;i++) if (abs(P[i].x-P[mid].x)<Ans) tmp[++pnt]=P[i]; else break;
  for (int i=mid-1;i>=l;i--) if (abs(P[i].x-P[mid].x)<Ans) tmp[++pnt]=P[i]; else break;
  sort(tmp+1,tmp+pnt+1,cmp);
  int L=1,R;
  for (R=3;R<=pnt;R++){
    while (abs(tmp[R].y-tmp[L].y)>=Ans) L++;
    for (int j=L;j<R;j++)
      for (int k=j+1;k<R;k++)
	Ans=min(Ans,Calc(tmp[j],tmp[k],tmp[R]));
  }
}

int main(){
  int xmax,xmin,ymax,ymin,xpymax,xpymin,xmymax,xmymin;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  xmax=ymax=xpymax=xmymax=-1<<30;
  xmin=ymin=xpymin=xmymin=1<<30;
  read(n);
  for (int i=1;i<=n;i++){
    P[i].read(); swap(P[i].x,P[i].y);
    xmax=max(xmax,P[i].x),xmin=min(xmin,P[i].x);
    ymax=max(ymax,P[i].y),ymin=min(ymin,P[i].y);
    xpymax=max(xpymax,P[i].x+P[i].y),xpymin=min(xpymin,P[i].x+P[i].y);
    xmymax=max(xmymax,P[i].x-P[i].y),xmymin=min(xmymin,P[i].x-P[i].y);
  }
  printf("%d\n",2*max(max(xpymax-xmin-ymin,xmax+ymax-xpymin),max(xmymax-xmin+ymax,xmax-ymin-xmymin)));
  sort(P+1,P+n+1);
  Solve(1,n);
  printf("%d\n",Ans*2);
  return 0; 
}


这道题正解是不是线段树啊 

我想了想三个点的相对位置 一共七八种吧 好麻烦

不过Claris把坐标转来转去 最终发现不对称的只有两种了

厉害啊
http://www.cnblogs.com/clrs97/p/4872267.html



跟最近点对很相似
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值