首页 > 其他分享 >P4557 [JSOI2018]战争 题解

P4557 [JSOI2018]战争 题解

时间:2023-05-25 22:14:27浏览次数:47  
标签:cnt ch P4557 题解 tot 凸包 int 夫斯基 JSOI2018

闵可夫斯基和

前言

入门建议看吉老师(吉如一)的计算几何入门到放弃。感觉应该是讲的最通俗易懂的了。

本文借鉴了Winxp的博客,以及吉老师视频中的思路。

写这篇博客的初衷是因为我作为一个初学者,此题里的题解对我来说理解起来不算太难,但是实现起来细节比较多,题解里也没有很详细地去解释(可能是因为我太菜了)。所以写这篇博客详细地解释一下这道题目里的一些操作。

定义

两个图形(也就是点集) \(A,B\) 的闵可夫斯基和定义为 \(C = \{a+b\mid a\in A,b\in B\}\)。

画一画图可以看出,闵可夫斯基和形成的图形就是一个图形 \(A\) 绕着图形 \(B\) 转一圈。

给个典图。

由于我比较菜,所以我们只考虑凸包的闵可夫斯基和的一些性质。

性质

一、

两个凸包的闵可夫斯基和还是凸包。

证明:

考虑凸集的一个性质:若 \(a,b\in A\),则 \(c = xa+(1-x)b ,c\in A(0\leq x \leq 1)\)。

设 \(a_1,a_2\in A,b_1,b_2 \in B\) ,根据闵可夫斯基和的定义,\(a_1+b_1,a_2+b_2\in C\)

任取一个

\[x\in[0,1],x(a_1+b_1)+(1-x)(a_2+b_2) \]

\[= [xa_1+(1-x)a_2] + [xb_1(1-x)b_2] \]

\[[xa_1+(1-x)a_2]\in A, [xb_1(1-x)b_2]\in B \]

证毕。

二、

闵可夫斯基和上的边是由两个凸包的边构成的

这个不好证,但是我们用瞪眼法看一看,应该是对的。

有了这两个性质,我们就能求闵可夫斯基和了。

求法

思路比较简单,但是细节比较多。

因为我们得到 \(A,B\) 两个凸包后,它们的极角序其实是已经排好了的,所以我们就可以采用类似于归并排序的策略,将两个凸包结合起来。

得到闵可夫斯基和后,因为有可能会出现三点共线的情况,所以要再求一次凸包。

il void Minkovski()
{
	for(re int i=1;i<n;i++) a[i] = s[i+1] - s[i]; a[n] = s[1] - s[n];//s是A凸包
	for(re int i=1;i<m;i++) b[i] = h[i+1] - h[i]; b[m] = h[1] - h[m];//h是B凸包
	int p1 = 1 , p2 = 1;
	tot = 1 , G[tot] = s[1] + h[1];//归并加入一下,G数组就是闵可夫斯基和
	while(p1 <= n && p2 <= m) tot++ , G[tot] = G[tot-1] + (a[p1]*b[p2]>=0 ? a[p1++]:b[p2++]);//极角序从小到大并从G[tot-1]开始往后接
	while(p1 <= n) tot++ , G[tot] = G[tot-1] + a[p1++];
	while(p2 <= m) tot++ , G[tot] = G[tot-1] + b[p2++];
}

我们用类似 a[i] = s[i+1]-s[i] 的操作得出每条边。再以 s[1]+h[1] 为起点开始往后加,每次加的时候就以 G[tot-1] 为基础往后接上。

应用

P4557 [JSOI2018]战争

一句话题意。

给定两个凸包 \(A,B\),\(q\) 次询问,每次给出一个向量 \(\overrightarrow{x}\),问 \(B\) 平移向量 \(\overrightarrow{x}\) 后,\(A\) 和 \(B\) 是否还有交集。\(n,m,q \leq 10^5\)。

原题上领地是看三角形内部的区域,实际上就是凸包。而问是否有交集,数学化后就有了下面的式子:

如果有交,那么

\[\exists b + \overrightarrow{x} = a(a\in A,b\in B) \]

转化一下,则有

\[\overrightarrow{x} \in \{a-b\mid a\in A,b\in B\} \]

我们看到这个很像 \(A,B\) 的闵可夫斯基和的形式,但是它们是相减,怎么办?把 \(B\) 取反,就变成闵可夫斯基和的形式了。

按照上述过程求出闵可夫斯基和后,接下来就剩下最后一个问题,如果判断一个点是否在凸包内,这个也比较好算,以图为例:

我们选定凸包上最左下角的点为起点,并将这个凸包平移一下,使得左下角的点坐落在原点上。这样就容易得出其他点的极角序了,我们按极角序进行二分,找到当前这个点与起点的连线位于哪两条线(凸包上的点与起点的连线)之间。然后如图连两条线,运用叉积运算判断这个点是在凸包外还是在凸包内即可。

另外还有一些小细节,我会在代码中写清。

#include<bits/stdc++.h>
//#define int long long
#define ll long long
#define next nxt
#define re register
#define il inline
const int N = 1e5 + 5;
const double eps = 1e-6;
const double Pi = acos(-1.0);
using namespace std;

struct Point{
	double x,y;
}a[N],b[N],s[N],h[N],A[N],G[N],d,st;
int n,m,q,tot;

il int read()
{
	int f=0,s=0;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) f |= (ch=='-');
	for(; isdigit(ch);ch=getchar()) s = (s<<1) + (s<<3) + (ch^48);
	return f ? -s : s;
}

Point operator +(Point a,Point b) { return Point{a.x+b.x,a.y+b.y}; }//加

Point operator -(Point a,Point b) { return Point{a.x-b.x,a.y-b.y}; }//减

il double operator *(Point a,Point b) { return a.x*b.y - a.y*b.x; }//叉积

il double operator &(Point a,Point b) { return a.x*b.x + a.y*b.y; }//点积

il double cross(Point a,Point b,Point c) { return (b-a)*(c-a); }//判断c和直线ab的关系 

il double len(Point a) { return sqrt(a&a); }

il bool cmp(Point a,Point b) { return a.x == b.x ? a.y < b.y : a.x < b.x; }//以x为第一关键字,y为第二关键字排序

il bool cmp2(Point a,Point b) { return a*b > 0 || (a*b == 0 && len(a) < len(b)); }//按极角序排序,如果极角相同短的放前边

il void ConvexHull(Point *s,Point *p,int &n)//s是凸包数组,p是原数组
{
	int cnt = 0;
	sort(p+1,p+n+1,cmp);
	s[++cnt] = p[1];
	for(re int i=2;i<=n;i++) //Andrew求凸包
	{
		while(cnt > 1 && cross(s[cnt-1],s[cnt],p[i]) <= 0) cnt--;
		s[++cnt] = p[i];
	}
	int t = cnt;
	for(re int i=n-1;i>=1;i--)
	{
		while(cnt > t && cross(s[cnt-1],s[cnt],p[i]) <= 0) cnt--;
		s[++cnt] = p[i];
	}
	n = cnt - 1;//第一个点加了两次,别忘了-1
}

il void Minkovski()
{
	memset(a , 0 , sizeof a);
	memset(b , 0 , sizeof b);//重复利用一下a,b数组,其实可以不memset
	for(re int i=1;i<n;i++) a[i] = s[i+1] - s[i]; a[n] = s[1] - s[n];//最后一条线单独写一下
	for(re int i=1;i<m;i++) b[i] = h[i+1] - h[i]; b[m] = h[1] - h[m];
	int p1 = 1 , p2 = 1;
	tot = 1 , G[tot] = s[1] + h[1];//以s[1]+t[1]为起点开始加
	while(p1 <= n && p2 <= m) tot++ , G[tot] = G[tot-1] + (a[p1]*b[p2]>=0 ? a[p1++]:b[p2++]);
	while(p1 <= n) tot++ , G[tot] = G[tot-1] + a[p1++];
	while(p2 <= m) tot++ , G[tot] = G[tot-1] + b[p2++];
}

il bool Judge(Point a)
{
	if(a*A[1] > 0 || A[tot]*a > 0) return 0;//绝对不可能在凸包内的情况
	int pos = lower_bound(A+1,A+tot+1,a,cmp2) - A - 1;//lower_bound求的是第一个极角序不小于它的,所以减个1
	return (a-A[pos])*(A[pos%tot+1]-A[pos])<=0;//如上文所说
}

signed main()
{
	n = read() , m = read() , q = read();
	for(re int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
	for(re int i=1;i<=m;i++) scanf("%lf%lf",&b[i].x,&b[i].y) , b[i].x = -b[i].x , b[i].y = -b[i].y;
	ConvexHull(s,a,n);
	ConvexHull(h,b,m);
	Minkovski();
	ConvexHull(A,G,tot);
	st = A[1];//选定一个基准点,并且把这个基准点平移到(0,0)的位置上,这样有利于我们求极角序
	for(re int i=1;i<=tot;i++) A[i] = A[i] - st;//别的也减一下
	while(q--)
	{
		scanf("%lf%lf",&d.x,&d.y);
		printf("%d\n",Judge(d-st));//这个也别忘了减
	}
	return 0;
}

由此可以看出,整个复杂度是 \(O((n+m+q)\log(n+m))\) 的,\(n,m,q\) 同阶,复杂度也就是 \(O(n\log n)\),可以通过。

标签:cnt,ch,P4557,题解,tot,凸包,int,夫斯基,JSOI2018
From: https://www.cnblogs.com/bloodstalk/p/17433102.html

相关文章

  • P4288 [SHOI2014]信号增幅仪 题解
    感谢审核人Description给定\(n\)个点,椭圆长轴的方向\(a\)和放大倍数\(p\),求覆盖全部点的最小椭圆的半短轴长度。Solution让我们求最小覆盖椭圆,但是椭圆不具有什么好的性质,我们可以把椭圆转化成圆来做,这样,题目就转化成了最小覆盖圆,这个用随机增量法来做就可以了。接下来......
  • UVA10902 Pick-up Sticks 题解
    Description按顺序给出\(n\)个棍子两个端点的坐标。如果后来的棍子与前边的棍子相交,则说后面的把前面的挡住了。问最后有多少个棍子没被挡住。\(n\leq10^5\),且答案不超过\(1000\)。Solution叉积基本运用。定义:\(\overrightarrow{a}\times\overrightarrow{b}=|\over......
  • SP898 Transmitters 题解
    Description给定\(n\)个点的坐标、半圆的半径以及坐标。问半圆怎么放能覆盖最多的点,输出最多个数。Solution计算几何入门题。首先显然距离圆心超过半径的点是一定不会被覆盖的,舍去。再者我们考虑,半圆的放法是有无限多种的,我们要考虑哪些是有用的。我们可以想到,最优的半圆一......
  • P8943 Deception Point 题解
    Description题目给的很详细了。Solution首先\(n\)个点\(n\)条边,我们很容易就想到基环树(比正常的树多了一条边,形成了一个环),不会也没关系,这题跟基环树其实关系不大。首先,我们可以发现题目中说明了这个环不是一个四元及以下的环,这代表着如果\(A\)提前进入了这个环,那么他......
  • [ABC287D] Match or Not 题解
    Description翻译给的很明白了,就是让你判断\(S\)串的前\(x(0\leqx\leq|T|)\)个字符和后\(|T|-x\)个字符组成的字符串和\(T\)串是否相等,其中问号能代替所有字母。Solution很有意思的一道题。首先我们可以知道,如果前\(i-1\)位不能匹配的话,那么第\(i\)位不管它本......
  • P5446 [THUPC2018]绿绿和串串 题解
    Description给定一个串\(S\),要求串\(S\)是串\(R\)经过多次翻转后的前缀。问有多少种初始长度的串\(R\)。串\(R\)翻转的定义是将前\(|R|-1\)个字符倒序排列后,插入到串的最后。如\(\mathrm{aaa}\)翻转后得到\(\mathrm{abcdcba}\)。Solution我们先设以\(i\)为......
  • CF1139E Maximize Mex 题解
    Description\(n\)个学生,\(m\)个社团。每个学生有一个能力值,且仅属于一个社团。这\(d\)天内,每天从\(m\)个社团中选人,使得选出的人的能力值的\(\text{mex}\)最大。每天会有一个人在选人之前退团。\(d,m\leqn\leq5000\)Solution巧妙建图题。首先,我们可以很显然的......
  • P8081 [COCI2011-2012#4] ZIMA 题解
    题意给定一个长度为\(n\)的序列。当连续\(T\)天温度都小于\(0\)时,则称这\(T\)天为一个冰期,冰期来临之前的\(2T\)天都被标记为警示状态.特殊地,如果一个冰期最长,那么它的前\(3T\)天会被标记为警示状态。如果有多个冰期最长,选一个。思路模拟预处理出每个冰期的长......
  • AT2395 [ARC071C] TrBBnsformBBtion 题解
    题目大意有两个只包含\(A\)和\(B\)的字符串,给出两种操作A可以变为BB,B可以变为A;AAA可以消去,BBB也可以消去。思路找规律。这里我们以A为主,将B全部变为A。因为可以无限次操作,那么就代表如果两个字符串可以相等,那么他们就一定能够经过转化后变成同一个字......
  • CF714B Filya and Homework 题解
    题意给定一个长度为\(n\)的数组。我们可以给一些数加上一个\(x\),也可以减去一个\(x\),也可以不加也不减。问:是否存在一个数\(x\),使得这个数组里各个数都相等。思路一道思维题首先考虑,在这个数组中,相同的元素,我们一定是给它做相同的操作,否则一定不相等,根据这个思想,......