先開個板子博再說。
除了計算幾何,其他板塊中的「凸包」都指「凸殼」。
開始以為專題裡「凸包」指的是計算幾何,就先把板子寫了。結果題全是斜率優化。。
兩個向量&
表示點積,*
表示叉積。
求二維凸包用的是 \(andrew\) 演算法,把凸包分成兩個凸殼維護。將點按座標排序後每次檢查當前點是否在當前棧頂線段的右側。如果在右側則當前點優於棧頂,彈棧。
判斷左右關係可以用叉積。正著做一遍倒著做一遍就可以得到乙個完整的凸包。
洛谷p2742[模板]二維凸包
#includeusing namespace std;
namespace io
while(ch>='0'&&ch<='9')
return f?-x:x;
} char output[50];
void write(ll x,char sp)while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}void ckmax(int& x,int y)
void ckmin(int& x,int y)
db dis(pot x,pot y)
int gethull(int top=1,int lim=0)
lim=top; hul[++top]=p[n-1];
for(int i=n-2;i;i--)
return top;
}} using namespace geometry;
signed main()
雖然好像不是凸包,但跟斜率相關主要是無腦且好用,還是提一下。很多斜率優化的題整出來式子後都可以直接上李超樹。下面很多維護凸包的方式實際上也能用李超樹代替。
只要沒什麼太毒瘤的限制一般斜率優化都可以用,平時的斜率優化式子交換 \(y\) 與縱截距 \(b\) 之後就能套到李超樹里。
好像可以區間查,但目前還不會···
區間插線段兩個 \(\log\) ,如果插直線則為乙個。
[noi2007]貨幣兌換
#includeusing namespace std;
namespace io
while(ch>='0'&&ch<='9')
return f?-x:x;
} char output[50];
void write(ll x,char sp)while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}void ckmax(ll& x,ll y)
void ckmin(ll& x,ll y)
void insert(seg v,int opl=1,int opr=n,int rt=1,int l=1,int r=n)
if(s[rt].val(h[l])>=v.val(h[l])&&s[rt].val(h[r])>=v.val(h[r])) return;
if(s[rt].val(h[mid])mid) insert(v,opl,opr,rd,mid+1,r);
}db query(int pos,int rt=1,int l=1,int r=n)
} using namespace lc_segmenttree;
signed main()
sort(h+1,h+n+1); build();
for(int i=1;i<=n;i++)
printf("%.3lf\n",f);
return 0;
}
對於一些帶有區間查詢,且需要斜率優化的題目,通常都可以用線段樹維護凸包解決。
如果題目可以離線,可以將所有待插入的點座標排序後依次插進線段樹。不然可以等線段樹區間內所有位置都存在點之後再pushup
來保證複雜度,因為不滿的區間一定不會做貢獻。
兩個凸包合併後形成的凸包一定都是原來凸包上的點,所以pushup
時直接把左右兒子區間的點全拿出來求一遍凸包即可。
[sdoi2014]向量集
#includeusing namespace std;
namespace io
while(ch>='0'&&ch<='9')
return f?-x:x;
} char output[50];
void write(ll x,char sp)while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}void ckmax(ll& x,ll y)
void ckmin(ll& x,ll y)
return hu[rt][res].x*v.x+hu[rt][res].y*v.y;
} else
return hd[rt][res].x*v.x+hd[rt][res].y*v.y;}}
void pushup(int rt)
tmp.clear();
for(auto x:hd[ld]) tmp.push_back(x);
for(auto x:hd[rd]) tmp.push_back(x);
sort(tmp.begin(),tmp.end());
for(auto x:tmp)
}void build(int rt=1,int l=1,int r=n)
void insert(int pos,pii v,int rt=1,int l=1,int r=n)
ll query(int opl,int opr,pii v,int rt=1,int l=1,int r=n)
} using namespace segmenttree;
signed main());
} else),'\n');}}
return 0;
}
當凸包需要動態維護(加點)時,需要平衡樹維護凸包。可以用set
維護凸包簡化**。
每次加點時向左右分別擴充套件,檢查左右的點是否需要被刪去,最後檢查當前點是否在凸包上。
[haoi2011]防線修建
#includeusing namespace std;
namespace io
while(ch>='0'&&ch<='9')
return f?-x:x;
} char output[50];
void write(ll x,char sp)while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}void ckmax(ll& x,ll y)
void ckmin(ll& x,ll y)
void del(sit v)
void add(pii u)
void insert(pii v)
while(!s.empty())
p=s.lower_bound(v);
sit q=p; --p;
if(cmp(*p,v,*q)) add(v);
}} using namespace hull;
signed main(); p[1].y=read();
m=read()+1; s.insert(); s.insert(); now=n;
for(int i=2;i<=m;i++) p[i]=, p[i].y=read();
q=read();
for(int i=1;i<=q;i++)
for(int i=1;i<=m;i++) if(!ban[i]) insert(p[i]);
for(int i=q;i;i--)
if(o[i].tp==1) insert(p[o[i].id]);
else ans[i]=now;
for(int i=1;i<=q;i++) if(o[i].tp==2) printf("%.2lf\n",ans[i]);
return 0;
}
在斜率優化時會遇到動態維護凸包的問題。這時除了使用set
維護凸包外,也可以用cdq分治解決。
具體流程:現將所有元素按斜率 \(k\) 排序,然後每次分治時將時間在前一半的元素放到左區間,遞迴左區間。
處理好左區間後解決左區間對右區間的貢獻。對左區間建出凸包,現在因為右區間斜率單調,可以省去凸包上二分。
因為要建凸包,所以處理完當前區間後按橫座標 \(x\) 遞增歸併排序。
[noi2007]貨幣兌換
#includeusing namespace std;
namespace io
while(ch>='0'&&ch<='9')
return f?-x:x;
} char output[50];
void write(ll x,char sp)while(x);
for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
}void ckmax(ll& x,ll y)
void ckmin(ll& x,ll y)
for(int i=mid+1;i<=r;i++)
solve(mid+1,r); merge_sort(l,r);
}signed main()
sort(d+1,d+n+1); solve(1,n);
printf("%.3lf\n",ans);
return 0;
}
斜率優化的各種板子
維護由若干點 x,y 構成上凸包,並支援求給定乙個斜率k的求通過某個點截距的最大值,需保證 x 遞增,詢問 k 遞減是用query,否則用query2,單次log n 判斷需要用到叉積,注意是否會爆掉ll。namespace ch point operator const point rhs con...
三維凸包板子 hdu 4273
hdu 4273 rescue 給乙個三維凸包,求重心到表面的最短距離 模板題 三維凸包 多邊形重心 點麵距離 include include include include includeusing namespace std const int maxn 550 const double eps...
查稅 斜率優化 單調佇列維護凸包 分塊 )
id3167 有n個辦公室,m個操作,依次讀入 type 如果type為1 接著讀入 t k z s 表示乙個公司於t時刻進駐k辦公室,每天盈利為z,其一開始有s元。若k位置本有別的公司,別的公司會被覆蓋。如果type為2 接著讀入t b 表示於t時刻,你要找出區間 a,b 內,最有錢的公司的錢數。...