计算几何相关
感觉没啥动力去学新知识了。
扫描线
扫描线直接照搬了...这篇主要是给之后的积分和凸包之类留的。
就是用线段树解决计算几何问题。
如何处理平面内一群矩形的面积交?
考虑用面积的朴素定义,
我们维护坐标系的
放个代码方便复习。
#include<bits/stdc++.h>
#define MAXN 1000005
#define int long long
using namespace std;
int n;
struct node{
int x1,x2,y;
int opt;
}sq[MAXN<<1];
inline bool cmp(node a,node b){
if(a.y==b.y)return a.opt>b.opt;
return a.y<b.y;
}
int mp[MAXN<<2];
int ans,tmp;
struct Segment_Tree{
#define ls(p) p<<1
#define rs(p) p<<1|1
struct TREE{
int l,r;
int val,tag;//维护一个当前x段的覆盖层数和贡献,只要有层数就有贡献,没层数就没贡献
}tree[MAXN<<3];
inline void build(int l,int r,int p){
tree[p].l=l,tree[p].r=r,tree[p].val=tree[p].tag=0;
if(l==r)return;
int mid=l+r>>1;
build(l,mid,ls(p));
build(mid+1,r,rs(p));
}
inline void push_up(int p){//主要看看pushup即可
if(tree[p].tag)tree[p].val=mp[tree[p].r+1]-mp[tree[p].l];
else{
tree[p].val=tree[ls(p)].val+tree[rs(p)].val;
}
}
inline void modify(int l,int r,int k,int p){
if(mp[tree[p].r+1]<=l||mp[tree[p].l]>=r)return;
if(mp[tree[p].l]>=l&&mp[tree[p].r+1]<=r){
tree[p].tag+=k;
push_up(p);
return;
}
// printf("nowmid=%lld,id=%lld,tree.l=%lld,tree.r=%lld\n",mid,p,tree[p].l,tree[p].r);
modify(l,r,k,ls(p));
modify(l,r,k,rs(p));
push_up(p);
}
}ST;
signed main(){
scanf("%lld",&n);
for(int i=1,x1,x2,y1,y2;i<=n;i++){
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
sq[i]=(node){x1,x2,y1,1};
sq[i+n]=(node){x1,x2,y2,-1};
mp[i]=x1,mp[i+n]=x2;
}
sort(mp+1,mp+1+n*2);
tmp=unique(mp+1,mp+1+2*n)-mp-1;
//printf("tmp=%lld\n",tmp);
sort(sq+1,sq+1+n*2,cmp);
ST.build(1,tmp-1,1);
//printf("ced\n");
for(int i=1;i<n*2;i++){
// printf("solving %lld %lld %lldf\n",sq[i].x1,sq[i].x2,sq[i].opt);
ST.modify(sq[i].x1,sq[i].x2,sq[i].opt,1);
ans+=ST.tree[1].val*(sq[i+1].y-sq[i].y);
}
printf("%lld",ans);
return 0;
}
现在让你计算那坨矩形的周长。
同样的分析方法,矩形的下底面和上底面都会对当前答案做出贡献,但是贡献是
然后考虑竖条对答案的贡献,当前线段树覆盖的一堆线段会形成许多个分开的块,具象下来就是当前
像是这张图虽然有三个矩形但只形成了两个块,因此实际的竖条个数是当前块数(2)乘以2。
用线段树再维护一下当前区段是否充满就可以推出来一共有几个块,满了就不加,不满就块数相加。
考虑用扫描线解决。
用矩形框点太蛋疼了,把点变成框那么大的矩形再用扫描线处理。
每个矩形相当于可以提供它权值的贡献,把这些贡献在
自适应辛普森积分
挺简单的。
暴力展开一个二次函数的积分形式。
考虑这样一件事,对于一个要求积的函数
所以提出了自适应辛普森积分,因为对于整个函数肯定有长得比较像二次函数的和长得不太像二次函数的。对于长得像二次函数的可以少递归几次保证复杂度,长得不像的就多递归几次保证正确性。所以分治地进行这个过程,因为二次函数劈两半还是二次函数,对于当前要处理的区间
另外有一些正确性和复杂度上的优化。比如可以考虑钦定一个无论如何都要达到的递归层数
附上 板子 代码
#include<bits/stdc++.h>
#define db double
using namespace std;
db a,b,c,d,L,R;
const db eps=1e-7;
inline db f(db x){
return (c*x+d)/(a*x+b);
}
inline db calc(db l,db r){
db mid=(l+r)/2;
return (r-l)*(f(l)+4*f(mid)+f(r))/6;
}
inline db asr(db l,db r,db eps,db ans){
// printf("nowlr=%.5f %.5f\n",l,r);
db mid=(l+r)/2.0;
db lv=calc(l,mid),rv=calc(mid,r);
if(fabs(lv+rv-ans)<=15*eps)return lv+rv+(lv+rv-ans)/15;
else return asr(l,mid,eps/2,lv)+asr(mid,r,eps/2,rv);
}
signed main(){
scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&L,&R);
printf("%.6f\n",asr(L,R,eps,calc(L,R)));
return 0;
}
考虑一种类似扫描线的思路,设
一样的,随便写写就过了。
一样的,随便写写就过了。
但是毕竟是道黑是吧所以还是稍微记一下。
脑玩一下发现影子拍上去圆面积不变只有
仍然积分法求,对于当前递归到而要求的
凸包
这些题真是计算几何吗我请问呢。
一眼分数规划,二分答案
发现
关于复杂度,凸壳二分自己带个
实现一个
考虑分块,维护单个块内的首项
是一个斜优的形式,考虑对整块维护凸包,散块暴力查。细节比较多。
考虑钦定一只妖怪为最高战力时的
进而维护上凸壳,
当且仅当
时等号成立,分讨一下在不在
显然可以建线段树处理,但是这样复杂度是假的,因为每添加一下都要重构
观察一下添加的形式:是按队列状添加的,而且询问肯定不会问还没添加的点,所以可以考虑先建出空的线段树,每次只有加入到当前区间的右端点时才push_up,这样复杂度就真了。
动态维护凸包,时间倒流成加点然后用set实现一下就行了。
相当于把鞋油dp转化到树上了
这个是链上的转移,考虑挪到树上怎么处理,就需要考虑点分树上原父亲所在的那个点,直接放代码吧。
#include<bits/stdc++.h>
#define MAXN 200005
#define int long long
#define db double
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
const auto inf=1e18;
int n,_;
int fa[MAXN],s[MAXN],p[MAXN],q[MAXN],l[MAXN];
int dp[MAXN],dis[MAXN];
db K[MAXN];
struct node{
int v,nxt;
}edge[MAXN<<1];
int h[MAXN],tmp;
inline void add(int u,int v){
edge[++tmp]=(node){v,h[u]};
h[u]=tmp;
}
bool vis[MAXN];
int sum,root,xv[MAXN],siz[MAXN];
inline void getrt(int u){
xv[u]=0;
siz[u]=1;
for(int i=h[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(vis[v])continue;
getrt(v);
siz[u]+=siz[v];
xv[u]=max(xv[u],siz[v]);
}
xv[u]=max(xv[u],sum-siz[u]);
if(root==-1||xv[u]<xv[root])root=u;
}
pii stac[2][MAXN];
int top[2];
inline void dfs(int u){
if(l[u]>dis[u])stac[0][++top[0]]=mp(l[u]-dis[u],u);
for(int i=h[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(vis[v])continue;
dis[v]=dis[u]+s[v];
dfs(v);
}
}
inline db slope(pii x,pii y){
return (db)(y.se-x.se)/(db)(y.fi-x.fi);
}
inline void solve(int D,int val){
while(top[1]>=2&&K[top[1]]>=slope(stac[1][top[1]],mp(D,val)))--top[1];
stac[1][++top[1]]=mp(D,val);
K[top[1]]=top[1]>=2?slope(stac[1][top[1]-1],stac[1][top[1]]):-inf;
}
inline int getans(int k){
int l=1,r=top[1],res=0;
while(r>=l){
int mid=l+r>>1;
if(K[mid]<=(db)k)res=stac[1][mid].se-k*stac[1][mid].fi,l=mid+1;
else r=mid-1;
}
return res;
}
inline void work(int u){
// printf("nowu=%lld\n",u);
root=-1;
getrt(u);
// printf("root=%lld\n",root);
vis[root]=1;
int mem=root;
// printf("mem=%lld\n",mem);
if(mem!=u)sum-=siz[mem],work(u);
top[0]=top[1]=dis[mem]=0;
// printf("ined\n");
dfs(mem);
// printf("oed\n");
sort(stac[0]+1,stac[0]+1+top[0]);
int tar=mem,sav=0;
// printf("top0=%lld\n",top[0]);
for(int i=1;i<=top[0];i++){
// printf("nowi=%lld\n",i);
int x=stac[0][i].fi,v=stac[0][i].se;
while(tar!=fa[u]&&sav+s[tar]<=x&&fa[tar])sav+=s[tar],solve(sav,dp[fa[tar]]),tar=fa[tar];
if(top[1])dp[v]=min(dp[v],dis[v]*p[v]+q[v]+getans(-p[v]));
}
for(int i=h[mem];i;i=edge[i].nxt){
int v=edge[i].v;
// printf("vis %d=%d\n",v,vis[v]);
if(vis[v])continue;
sum=siz[v];
work(v);
}
}
signed main(){
scanf("%lld%lld",&n,&_);
for(int i=2;i<=n;i++){
dp[i]=inf;
scanf("%lld%lld%lld%lld%lld",&fa[i],&s[i],&p[i],&q[i],&l[i]);
add(fa[i],i);
}
sum=n;vis[0]=1;
work(1);
for(int i=2;i<=n;i++)printf("%lld\n",dp[i]);
return 0;
}
大抵是出题人发现加了yz太难所以给扔了
考虑一个线段树分治类似的思路,把一个星球存在的时间段放到线段树上维护,查询就是查询那个时间点的星球存在情况。
但是这样时空显然是炸的,怎么优化一下,式子转化为
是一个斜优的形式,且是一个斜率递增的凸包,那就可以给询问的
细节有点多放个码
#include<bits/stdc++.h>
#define ll long long
#define MAXN 500005
#define db double
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
int n,q;
ll C[MAXN],X[MAXN];
struct node{
int v,nxt;
}edge[MAXN];
int h[MAXN],tmp;
inline void add(int u,int v){
edge[++tmp]=(node){v,h[u]};
h[u]=tmp;
}
int opt[MAXN],dfn[MAXN],tim;
vector<int>L[MAXN],R[MAXN];
inline void dfs(int u){
dfn[u]=++tim;
if(opt[u]>0)L[opt[u]].emplace_back(tim);
else R[-opt[u]].emplace_back(tim-1);
for(int i=h[u];i;i=edge[i].nxt){
int v=edge[i].v;
dfs(v);
}
if(opt[u]>0)R[opt[u]].emplace_back(tim);
else L[-opt[u]].emplace_back(tim+1);
}
const ll inf=2e18;
inline db slope(int x,int y){
return (db)((X[x]*X[x]+C[x])-(X[y]*X[y]+C[y]))/(X[x]-X[y]);
}
struct Segment_Tree{
#define ls(p) p<<1
#define rs(p) p<<1|1
#define h(p) tree[p].h
#define t(p) tree[p].t
#define sav(p) tree[p].sav
struct TREE{
int h,t;
vector<int>sav;
}tree[MAXN<<2];
inline void build(int l,int r,int p){
h(p)=0,t(p)=-1;
if(l==r)return ;
int mid=l+r>>1;
build(l,mid,ls(p));
build(mid+1,r,rs(p));
}
inline void modify(int l,int r,int ul,int ur,int id,int p){
if(l>=ul&&r<=ur){
while(sav(p).size()<=t(p)+5)sav(p).emplace_back(0);
if(t(p)>=h(p)&&X[sav(p)[t(p)]]==X[id]){
if(C[sav(p)[t(p)]]<=C[id])return ;
--t(p);
}
while(t(p)>h(p)&&slope(sav(p)[t(p)],id)<slope(sav(p)[t(p)],sav(p)[t(p)-1]))--t(p);
sav(p)[t(p)+1]=id;++t(p);
return ;
}
int mid=l+r>>1;
if(ul<=mid)modify(l,mid,ul,ur,id,ls(p));
if(ur>mid)modify(mid+1,r,ul,ur,id,rs(p));
}
inline ll query(int l,int r,int x,ll v,int p){
while(t(p)-h(p)+1>=2&&slope(sav(p)[h(p)],sav(p)[h(p)+1])<=(db)v*2.0)++h(p);
ll res=inf;
// printf("p=%lld h=%lld t=%lld\n",p,h(p),t(p));
if(t(p)-h(p)+1>=1&&sav(p).size())res=(v-X[sav(p)[h(p)]])*(v-X[sav(p)[h(p)]])+C[sav(p)[h(p)]];
if(l==r)return res;
int mid=l+r>>1;
if(x<=mid)res=min(res,query(l,mid,x,v,ls(p)));
else res=min(res,query(mid+1,r,x,v,rs(p)));
return res;
}
}ST;
int idx[MAXN];
inline bool cmp(int x,int y){
return X[x]<X[y];
}
struct Que{
ll x;
int u,id;
bool operator<(const Que &a)const{
return x<a.x;
}
}que[MAXN];
ll ans[MAXN];
signed main(){
scanf("%d%d%lld",&n,&q,&C[0]);
for(int i=2,typ,f,idx,_1,_2;i<=n;i++){
scanf("%d%d%d",&typ,&f,&idx);
++f;
add(f,i);
if(!typ){
scanf("%lld%d%d%lld",&X[idx],&_1,&_2,&C[idx]);
opt[i]=idx;
}
else opt[i]=-idx;
}
for(int i=1;i<=q;i++){
scanf("%d%lld",&que[i].u,&que[i].x);++que[i].u;
que[i].id=i;
}
dfs(1);
ST.build(1,n,1);
for(int i=1;i<=n;i++)idx[i]=i;
sort(idx+1,idx+1+n,cmp);
sort(que+1,que+1+q);
ST.modify(1,n,1,n,0,1);
for(int i=1,u;i<=n;i++){
u=idx[i];
// printf("nowu=%lld\n",u);
for(int o=0,l,r;o<L[u].size();o++){
l=L[u][o],r=R[u][o];
// printf("l=%lld r=%lld\n",l,r);
if(l>r)continue;
ST.modify(1,n,l,r,u,1);
}
}
for(int i=1;i<=q;i++)ans[que[i].id]=ST.query(1,n,dfn[que[i].u],que[i].x,1);
for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
return 0;
}
半平面交
多边形面积交,考虑用辛普森积分解决。
主要用向量相关和凸包解决这个问题。给一个平面向量的封装,cpr就是向量叉积,三个元素就是拿后两个给第一个作差算的,相当于过一交点的线段的叉积。
struct node{
db x,y;
node(){}
node(db a,db b){x=a,y=b;}
bool operator<(const node &a)const{if(y==a.y)return x<a.x;return y<a.y;}
bool operator==(const node &a)const{return x==a.x&&y==a.y;}
node operator-(const node &a)const{return node(x-a.x,y-a.y);}
}sav[MAXN],Cx[MAXN];
inline db cpr(node a,node b){return a.x*b.y-a.y*b.x;}
inline db cpr(node a,node b,node c){return cpr(b-a,c-a);}
极角排序
可以用 atan2(y,x)
求得
排好序之后对贡献的向量只保留最靠近可行域的那个,半平面交最后会是一个凸多边形的形式,进而考虑对保留下来的向量和交点维护凸包。
具体地维护有以下情况:
比如说现在逐个加入
然后闭合凸包的时候
后来的向量可能会踢掉最开始若干个点和向量,总之就是实现一个双端队列就能维护。
闭合之后还得用队首踢一下队尾,因为队尾还没限制。
板题里边让你求面积,搓出来之后三角剖分一下就好了。
题:学校oj上没人做这块,我直接把写的题解抄过来了。
赛车
对于正解:车
考虑更简单的做法:由于
铁人三项(Saber vs Lancer)
设给定三个速度为
钦定一个
考虑将
另外
逃考
首先,样例中第二个输入有误,出现了一个边界外的亲戚。
其次,小杨可以没有亲戚。
Voronoi 图由一组由连接两邻点直线的垂直平分线组成的连续多边形组成,根据
个在平面上不重合种子点,把平面分成 个区域,使得每个区域内的点到它所在区域的种子点的距离比到其它区域种子点的距离近。
亲戚的管辖范围形成了 Voronoi图。进一步地,如果对 Voronoi图 上接壤的亲戚连边,对和边界接壤的亲戚连接到汇点就可以用最短路解决这个问题。
考虑如何判断亲戚二元组
所以维护每个亲戚与其他亲戚连线中垂线的半平面交并对每个边备注所属的亲戚编号方便后续建图,半平面交部分的复杂度是
小凸想跑步
想一下应该就是:点
假设p01的三角形为
设
原来写这个式子的草纸没了,这个是现敲的,可能会错但是反正就是那么个一次函数线性规划的形式,你自己推一下得了嘻嘻。
然后你算一下相邻点的横纵坐标差拿推出来的式子跑一下半平面交,记得把原多边形也塞进去跑半平面交,理论上这样跑完三角剖分求面积作比就完事了。
但是这个题被卡精也是不可不品鉴的一环嘻嘻,数据我已经让教练帮忙搞到文件里了你自己拍拍乐吧然后提供一些可能有用的注意事项。
- 第一次见到神人题精度开高会死的,如果你eps在1e-9以下得30/70pts试一下把eps换成1e-8,1e-7这一类,之前开1e-14发现极角排序没有相同斜率直线结果凸包里一堆平行线呃呃。
- 这个题的向量很容易有横线竖线可以用点+斜率的方式表示向量。
- 不过要硬用两点表示向量也是ok的你拿着式子注意一下
是不是快为0是的话就是一个 的形式按 正负分讨就行。
Poi2009 Wsp-island
显然可以把
发现给出的图是一个凸多边形,所以越是靠近
最后把
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了