凸包

简析

选中左下的点,按极角排序(叉积),然后按次序加入(边为逆时针)

模板

A. 【例题1】凸包面积 - 「计算几何」第2章 凸包 - 金牌导航 - 课程 - YbtOJ

#include<bits/stdc++.h>
#define db double
const int INF=1e9;
using namespace std;

const int N=2e4+5;
int n,top;
struct Po {int x,y; }a[N],s0,st[N];
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline int operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline int dis(Po i) {
	return i.x*i.x+i.y*i.y;
}
inline bool cmp(Po i,Po j) {
	return (i-s0)*(j-s0)>0||(i-s0)*(j-s0)==0&&dis(i-s0)<dis(j-s0);
}

int ask() {
	int ret=0;
	for(int i=1,j=top;i<=top;j=i,i++) {
		ret=ret+st[j]*st[i];
	}
	return fabs(ret)>>1;
}
int main() {
	scanf("%d",&n); s0=(Po){INF,INF};
	for(int i=1;i<=n;i++) {
		scanf("%d%d",&a[i].x,&a[i].y);
		if(s0.x>a[i].x||s0.x==a[i].x&&s0.y>a[i].y) {
			s0=a[i];
		}
	}
	sort(a+1,a+n+1,cmp);
	st[++top]=a[1],st[++top]=a[2];
	for(int i=3;i<=n;i++) {
		while(top>1&&(a[i]-st[top])*(st[top]-st[top-1])>=0) {
			top--;
		}
		st[++top]=a[i];
	}
	printf("%d\n",(int)floor(ask()/50));
	return 0;
}

例题1

[P3829 SHOI2012]信用卡凸包 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

大胆feel一下:所有转角加起来为\(2\pi\)

然后对圆心求凸包

#include<bits/stdc++.h>
#define db long double
const db INF=1e9,Pi=acos(-1),eps=1e-9;;
using namespace std;

const int N=3e5+5;
int n,m,top;
struct Po {db x,y; }a[N],st[N];
inline Po operator +(Po i,Po j) {
	return (Po){i.x+j.x,i.y+j.y};
}
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline db operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,db j) {
	return (Po){i.x*j,i.y*j};
}
inline db dis(Po i) {
	return sqrt(i.x*i.x+i.y*i.y);
}
inline bool cmp(Po i,Po j) {
	return (i-a[1])*(j-a[1])>0||(i-a[1])*(j-a[1])==0&&dis(i-a[1])<dis(j-a[1]);
}
inline Po turn (Po t,db aph) {
	db co=cos(aph),si=sin(aph);
	return (Po){t.x*co-t.y*si,t.x*si+t.y*co};
}
int main() {
	db d,w,r;
	scanf("%d%Lf%Lf%Lf",&n,&w,&d,&r); d=d/2-r,w=w/2-r;
	Po t1=(Po){d,w},t2=(Po){-d,w},t3=(Po){-d,-w},t4=(Po){d,-w};
	for(int i=1;i<=n;i++) {
		db aph; Po t;
		scanf("%Lf%Lf%Lf",&t.x,&t.y,&aph);
		a[++m]=t+turn(t1,aph);
		a[++m]=t+turn(t2,aph);
		a[++m]=t+turn(t3,aph);
		a[++m]=t+turn(t4,aph);
	}
	for(int i=2;i<=m;i++) {
		if(a[i].x<a[1].x||a[i].x==a[1].x&&a[i].y<a[1].y) {
			swap(a[i],a[1]);
		}
	}
	sort(a+2,a+m+1,cmp);
	st[++top]=a[1],st[++top]=a[2];
	for(int i=3;i<=m;i++) {
		while(top>1&&(a[i]-st[top])*(st[top]-st[top-1])>=eps) {
			top--;
		}
		st[++top]=a[i];
	}
	db ans=Pi*r*2;
	for(int i=1,j=top;i<=top;j=i,i++) {
		ans+=dis(st[i]-st[j]);
	}
	printf("%.2Lf\n",ans);
	return 0;
}

例题2

D. 稳定凸包 - 「计算几何」第2章 凸包 - 金牌导航 - 课程 - YbtOJ

网上题解全tm是错的,而且都错的一样,他们都是SB吧

显然如果一个凸包稳定,则必然每条边上都有一个点(不包括端点)

PS:为什么两个点一条边不能是一个凸包呢?想不明白

而他保证了是一个凸包,所以极角排序后一定是image-20210427091221852

只有两端可能是两条线,所以特判两端一定是两条线,中间也一定要有限

WA了一次:没特判只有两个点的情况

#include<bits/stdc++.h>
#define ll long long 
const ll INF=1e9;
using namespace std;

const int N=3e5+5;
int n,m,top,st[N];
struct Po {ll x,y; }a[N];
inline Po operator +(Po i,Po j) {
	return (Po){i.x+j.x,i.y+j.y};
}
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline ll operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}
inline Po operator *(Po i,ll j) {
	return (Po){i.x*j,i.y*j};
}
inline ll dis(Po i) {
	return i.x*i.x+i.y*i.y;
}
inline bool cmp(Po i,Po j) {
	return (i-a[1])*(j-a[1])>0||(i-a[1])*(j-a[1])==0&&dis(i-a[1])<dis(j-a[1]);
}
inline Po turn (Po t,ll aph) {
	ll co=cos(aph),si=sin(aph);
	return (Po){t.x*co-t.y*si,t.x*si+t.y*co};
}
bool fl[N];
int main() {
int T; scanf("%d",&T);
while(T--) {
	scanf("%d",&n); 
	for(int i=1;i<=n;i++) {
		scanf("%lld%lld",&a[i].x,&a[i].y); 
	}
	if(n<=6) {
		puts("NO"); continue;
	}
	for(int i=2;i<=n;i++) {
		if(a[i].x<a[1].x||a[i].x==a[1].x&&a[i].y<a[1].y) {
			swap(a[i],a[1]);
		}
	}
	sort(a+2,a+n+1,cmp);
	if((a[1]-a[n])*(a[1]-a[n-1])) {
		puts("NO"); continue;
	}
	for(;n>=2&&(a[1]-a[n])*(a[1]-a[n-1])==0;n--) a[n-1]=a[n]; 
	bool fll=0;
	for(int i=2;i<n-1;i++) {
		if((a[i+1]-a[i])*(a[i-1]-a[i])&&(a[i+1]-a[i])*(a[i+2]-a[i+1])) {
			puts("NO"); fll=1; break;
		}
	}
	if(!fll&&(a[n]-a[n-1])*(a[n-1]-a[n-2])) {
		puts("NO"); fll=1;
	}
	if(!fll) puts("YES");
}
	return 0;
}

例题3

P4192 旅行规划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

区间加上等差数列,区间加,区间求max

等差数列具有可加性和可裂性,当场想到线段树,可无法支持区间求max,所以试试万能的分块

考虑一个块内如何快速地求出max

设块要加上\(a_1+(i-1)d\)

不妨设最后的结果为\(Ans=Max\{b_i+id\}+a_1-d\),发现这是\(Ans[i]=i\times j+j\)的形式,考虑斜率优化

可发现:\(b_i=-id+a_1-d+Ans\)

所以维护\((i,b[i])\)的凸包,然后二分即可

每次暴力改两端的凸包,暴力对后面的凸包加,暴力改中间凸包的max值

#include<bits/stdc++.h>
#define ll long long 
const ll INF=1e18; 
using namespace std;

const int N=1e5+5,M=350;
int n,bl[N]; ll b[N];
struct Po{ll x,y; };
inline Po operator -(Po i,Po j) {
	return (Po){i.x-j.x,i.y-j.y};
}
inline ll operator *(Po i,Po j) {
	return i.x*j.y-i.y*j.x;
}

struct A{
	int n,top,st[M];
	ll a1,d,add;
	Po a[M];
	inline void ins(int x,ll y) {
		a[x]=(Po){x,y};
	}
	inline void make() {
		st[top=1]=1;
		for(int i=2;i<=n;i++) {
			while(top>1&&(a[st[top]]-a[st[top-1]])*(a[i]-a[st[top]])>=0) {
				top--;
			}
			st[++top]=i;
		}
	}
	inline ll ask(int x) {
		return a[x].y+d*(x-1)+a1+add;
	}
	inline ll get() {
		int l=1,r=top-1,ret=top; Po t;
		while(l<=r) {
			int mid=l+r>>1; t=a[st[mid+1]]-a[st[mid]];
			if(t.y<=-d*t.x) {
				ret=mid; r=mid-1;
			} else l=mid+1;
		}
		return ask(st[ret]);
	}
}V[M];
int main() {
	scanf("%d",&n); int S=sqrt(n);
	for(int i=1;i<=n;i++) {
		bl[i]=(i-1)/S+1;
		scanf("%lld",&b[i]),b[i]+=b[i-1];
		V[bl[i]].ins(i-(bl[i]-1)*S,b[i]);
	}
	for(int i=1;i<=bl[n];i++) {
		V[i].n=min(bl[i]*S,n)-(bl[i]-1)*S;
		V[i].make();
	}
	int T; scanf("%d",&T);
	while(T--) {
		int op,x,y; scanf("%d%d%d",&op,&x,&y);
		if(op==0) {
			ll k; scanf("%lld",&k);
			if(bl[x]==bl[y]) {
				for(int i=x;i<=y;i++) {
					b[i]+=k*(i-x+1);
					V[bl[i]].ins(i-(bl[i]-1)*S,b[i]);
				}
				ll t=k*(y-x+1);
				for(int i=y+1;i<=min(bl[y]*S,n);i++) {
					b[i]+=t;
					V[bl[i]].ins(i-(bl[i]-1)*S,b[i]);
				}
				for(int i=bl[y]+1;i<=bl[n];i++) {
					V[i].add+=t;
				}
				V[bl[x]].make();
				continue;
			}
			int t1=bl[x]+1,t2=bl[y]-1;
			if(t1<=t2) {
				ll t=k*((t1-1)*S+1-x+1);
				for(int i=t1;i<=t2;i++) {
					V[i].a1+=t;
					V[i].d+=k;
					t+=k*S;
				}
			}
			for(int i=x;i<=bl[x]*S;i++) {
				b[i]+=k*(i-x+1);
				V[bl[i]].ins(i-(bl[i]-1)*S,b[i]);
			}
			V[bl[x]].make();
			for(int i=t2*S+1;i<=y;i++) {
				b[i]+=k*(i-x+1);
				V[bl[i]].ins(i-(bl[i]-1)*S,b[i]);
			}
			ll t=k*(y-x+1);
				for(int i=y+1;i<=min(bl[y]*S,n);i++) {
					b[i]+=t;
					V[bl[i]].ins(i-(bl[i]-1)*S,b[i]);
				}
				for(int i=bl[y]+1;i<=bl[n];i++) {
					V[i].add+=t;
				}
			V[bl[y]].make();
		} else {
			ll ans=-INF;
			if(bl[x]==bl[y]) {
				for(int i=x;i<=y;i++) {
					ans=max(ans,V[bl[i]].ask(i-(bl[i]-1)*S));
				}
				printf("%lld\n",ans);
				continue;
			}
			int t1=bl[x]+1,t2=bl[y]-1;
			for(int i=t1;i<=t2;i++) {
				ans=max(ans,V[i].get());
			}
			for(int i=x;i<=bl[x]*S;i++) {
				ans=max(ans,V[bl[i]].ask(i-(bl[i]-1)*S));
			}
			for(int i=t2*S+1;i<=y;i++) {
				ans=max(ans,V[bl[i]].ask(i-(bl[i]-1)*S));
			}
			printf("%lld\n",ans);
		}
	}
	return 0;
}
posted @ 2021-04-27 09:28  wwwsfff  阅读(87)  评论(0编辑  收藏  举报