凸包
简析
选中左下的点,按极角排序(叉积),然后按次序加入(边为逆时针)
模板
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:为什么两个点一条边不能是一个凸包呢?想不明白
而他保证了是一个凸包,所以极角排序后一定是
只有两端可能是两条线,所以特判两端一定是两条线,中间也一定要有限
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;
}