P2497 [SDOI2012] 基站建设
题意
平面上有很多点,第 \(i\) 个点的坐标为 \((x_i,0)\),每个点有两个属性 \(r_i,v_i\),分别表示祂的接受范围和选择代价。
现在你需要选择一些点 \(a_1,a_2,\dots,a_k\),满足 \(x_k+r_k\ge m,a_1=1\),且链接相邻两个点的代价的总和最小。
连接两个点 \((i,j)(i<j)\) 的代价为 \(\sqrt{r'}\),满足圆心在 \((x_i,r_i)\),半径为 \(r_i\) 的圆和圆心在 \(x_j,r'\),半径为 \(r'\) 的园相切。
选择某个点 \(i\) 的代价为 \(v_i\)。
求最小的代价总和。
\(n\le 5\times10^5,x_i,m\le 10^{12},v_i\le 10^4\)。
思路
考虑两个点 \((i,j)(i<j)\) 连接的代价。把那两个圆的圆心相连,变成一个直角三角形,斜边长度为 \(r_i+r'\),两条直角边分别为 \(x_j-x_i\) 和 \(|r'-r_j|\)。根据勾股定理,有 \((r'-r_j)^2+(x_j-x_i)^2=(r_i+r')^2\),化简得 \(r'=\frac{(x_j-x_i)^2}{4r_i}\),代价即为 \(\frac{x_j-x_i}{2\sqrt{r_i}}\)。
设 \(f_i\) 表示从 \(1\) 号点连到 \(i\)(\(i\) 必须选)的最小代价。容易得到转移 \(f_i=\min_{j<i}(f_j+\frac{x_i-x_j}{2、sqrt{r_j}})\),用李超线段树优化一下即可。
代码
由于 \(x\) 很大,需要离散化。
/*
Luogu P2497 [SDOI2012] 基站建设
2026-04-16
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
template<typename T>
inline void read(T&x){
x=0;char c=getchar();bool f=0;
while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
f?x=-x:0;
}
template<typename T>
inline void write(T x){
if(x==0){putchar('0');return ;}
x<0?x=-x,putchar('-'):0;short st[50],top=0;
while(x) st[++top]=x%10,x/=10;
while(top) putchar(st[top--]+'0');
}
inline void write(double x){printf("%lf",x);}
inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
inline void write(char c){putchar(c);}
inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define LL long long
const int maxn=500010;
const double inf=100000000000000000;
double f[maxn],ans=inf;
int n;
LL m,p[maxn];
struct node{LL x;int r,v,id;}a[maxn];
struct Line{
double k,b;
double calc(int w){double x=p[w];return k*x+b;}
};
class LICHAO_Segment_Tree{
private:
Line t[maxn*8];
void insert(int u,int l,int r,Line x){
int mid=l+r>>1;
if(x.calc(mid)<t[u].calc(mid)) swap(t[u],x);
if(l==r) return ;
if(x.calc(l)<t[u].calc(l)) insert(u<<1,l,mid,x);
else insert(u<<1|1,mid+1,r,x);
}
double query(int u,int l,int r,int d){
if(l>d||r<d) return inf;
if(l==r) return t[u].calc(d);
int mid=l+r>>1;
return min({t[u].calc(d),query(u<<1,l,mid,d),query(u<<1|1,mid+1,r,d)});
}
public:
LICHAO_Segment_Tree(){for(int i=1;i<=maxn*4;i++) t[i]={0,inf};}
void insert(Line x){insert(1,1,n,x);}
double query(int x){return query(1,1,n,x);}
}t;
signed main(){
read(n,m);
for(int i=1;i<=n;i++) read(a[i].x,a[i].r,a[i].v),p[i]=a[i].x;
sort(p+1,p+1+n);
for(int i=1;i<=n;i++) a[i].id=lower_bound(p+1,p+1+n,a[i].x)-p;
f[1]=a[1].v;
t.insert({1/(2*sqrt(a[1].r)),f[1]-a[1].x/(2*sqrt(a[1].r))});
for(int i=2;i<=n;i++){
f[i]=t.query(a[i].id)+a[i].v;
t.insert({1/(2*sqrt(a[i].r)),f[i]-a[i].x/(2*sqrt(a[i].r))});
}
for(int i=1;i<=n;i++) if(a[i].x+a[i].r>=m) ans=min(ans,f[i]);
printf("%.3lf",ans);
return 0;
}

浙公网安备 33010602011771号