1. 程式人生 > >[BZOJ5317][JSOI2018]部落戰爭(閔可夫斯基和)

[BZOJ5317][JSOI2018]部落戰爭(閔可夫斯基和)

\n [] using include operator typedef pac def class

對於點集$A$,$B$,閔可夫斯基和$C=\{(x1+x2,y1+y2)|(x1,x2)\in A,(y1,y2)\in B\}$。
由此可知,對於兩個凸包$A$,$B$的閔可夫斯基和$C$滿足,$C$中的向量是所有$A$中向量與$B$中向量的和的並集。可以證明,$C$也是一個凸包。
現在問題是要求,對於詢問向量$\vec{d}$,是否存在$\vec{a}\in A$,$\vec{b}\in B$,使得$\vec{a}=\vec{b}+\vec{d}$。
移項得$\vec{d}=\vec{a}-\vec{b}$,發現這是$B$中所有向量取反後與$A$的閔可夫斯基和。於是問題轉化為,求$A$與$-B$的閔可夫斯基和$C$,並快速判斷某個向量是否在$C$內。


求閔可夫斯基和有一個線性算法,正確性不會證明。
先求出$A$和$B$的凸包,再取出凸包上所有的邊,這些邊顯然都分別是已經按極角排好序的向量。
初始時$C$只有一個點$\vec{a1}+\vec{b1}$,其中$a1$,$b1$分別是$A$和$B$凸包中的第一個點。
接下來類似歸並排序地,按極角序從小到大依次插入新向量並維護凸包。
最後,凸包的尾部可能會出現一些多余點,直接刪除或再求一次凸包即可。
得到凸包$C$後,問題只剩快速判斷一個點$P$是否在凸包$C$內了。
很簡單,將凸包中點以相對於凸包第一個點的極角序排好,二分找到$P$在其中哪個位置,向量叉積即可判斷。
總復雜度$O(n\log n)$

 1
#include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 typedef long long ll; 5 using namespace std; 6 7 const int N=200010; 8 int n,m,q,tot; 9 struct P{ ll x,y; }s,p[N],p1[N],p2[N],v1[N],v2[N],t[N],a[N]; 10 P operator +(const P &a,const
P &b){ return (P){a.x+b.x,a.y+b.y}; } 11 P operator -(const P &a,const P &b){ return (P){a.x-b.x,a.y-b.y}; } 12 ll operator *(const P &a,const P &b){ return a.x*b.y-a.y*b.x; } 13 ll len(P a){ return a.x*a.x+a.y*a.y; } 14 15 bool cmp(const P &a,const P &b){ ll k=(a-s)*(b-s); return k ? k>0 : len(a-s)<len(b-s); } 16 17 int Graham(P a[],int n){ 18 rep(i,1,n) t[i]=a[i]; 19 rep(i,2,n) if (t[i].x<t[1].x || (t[i].x==t[1].x && t[i].y<t[1].y)) swap(t[1],t[i]); 20 s=t[1]; sort(t+2,t+n+1,cmp); int top=1; 21 rep(i,2,n){ 22 while (top>1 && (t[top]-t[top-1])*(t[i]-t[top-1])<=0) top--; 23 t[++top]=t[i]; 24 } 25 rep(i,1,top) a[i]=t[i]; 26 return top; 27 } 28 29 bool jud(P x){ 30 P vx=x-p[1]; 31 if (vx*(p[tot]-p[1])<0 || vx*(p[2]-p[1])>0) return 0; 32 int px=lower_bound(p+2,p+tot+1,x,cmp)-p-1; 33 return (x-p[px])*(p[px%tot+1]-p[px])<=0; 34 } 35 36 int main(){ 37 freopen("bzoj5317.in","r",stdin); 38 freopen("bzoj5317.out","w",stdout); 39 scanf("%d%d%d",&n,&m,&q); 40 rep(i,1,n) scanf("%lld%lld",&p1[i].x,&p1[i].y); 41 rep(i,1,m) scanf("%lld%lld",&p2[i].x,&p2[i].y),p2[i].x=-p2[i].x,p2[i].y=-p2[i].y; 42 n=Graham(p1,n); m=Graham(p2,m); 43 rep(i,1,n-1) v1[i]=p1[i+1]-p1[i]; v1[n]=p1[1]-p1[n]; 44 rep(i,1,m-1) v2[i]=p2[i+1]-p2[i]; v2[m]=p2[1]-p2[m]; 45 p[1]=p1[1]+p2[1]; tot=1; 46 int l1=1,l2=1; 47 while (l1<=n || l2<=m) 48 tot++,p[tot]=p[tot-1]+((l1<=n && (l2>m || (v1[l1]*v2[l2]>=0))) ? v1[l1++] : v2[l2++]); 49 while (tot>1 && (p[tot]-p[tot-1])*(p[1]-p[tot-1])<=0) tot--; 50 s=p[1]; P qs; 51 rep(i,1,q) scanf("%lld%lld",&qs.x,&qs.y),printf("%d\n",jud(qs)); 52 return 0; 53 }

[BZOJ5317][JSOI2018]部落戰爭(閔可夫斯基和)