题解:P15619 [ICPC 2022 Jakarta R] City Hall
问题重述
给定一个无向连通图,每个点有一个初始海拔
你可以将至多一个点的海拔改为任意非负实数,求从
解题思路
1. 预处理原图最短路
在未修改时,边权非负,因此可以用 Dijkstra 求出:
原图的最短路为
2. 考虑修改一个点后的影响
设我们修改点
若最优路径经过点
其中
由于边权非负,任何最优路径都可以写成这种分解形式(若
对固定的
因此,修改点
其中
最终答案还需与
3. 李超线段树优化
由于直接枚举所有点
复杂度分析
- Dijkstra:
O(M\log N) 。 - 李超树操作:每个点插入
deg(i) 条直线,查询deg(i) 次,总操作数O(M) ,每次O(\log H) 。 - 总时间复杂度:
O(M\log N+M\log H) 。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=2e6+10;
const long long inf=4e18;
typedef long long ll;
typedef pair<ll,int> PLI;
vector<int> q[N];
ll a[N],b[N],h[N],maxn,ans;
int vis[N],idx,cnt,tr[4*N],ver[4*N],cur;
struct node{
ll k,b;
}q1[4*N];
void dijkstra(int s,int t){
priority_queue<PLI,vector<PLI>,greater<PLI> > p;
memset(a,0x3f,sizeof a);
memset(b,0x3f,sizeof b);
a[s]=0,p.push({0,s});
while(!p.empty()){
ll dis=p.top().first;
int x=p.top().second;
p.pop();
if(vis[x]) continue;
vis[x]=1;
for(int y:q[x]){
ll w=dis+(h[x]-h[y])*(h[x]-h[y]);
if(w<a[y]){
a[y]=w;
p.push({w,y});
}
}
}
b[t]=0,p.push({0,t});
memset(vis,0,sizeof vis);
while(!p.empty()){
ll dis=p.top().first;
int x=p.top().second;
p.pop();
if(vis[x]) continue;
vis[x]=1;
for(int y:q[x]){
ll w=dis+(h[x]-h[y])*(h[x]-h[y]);
if(w<b[y]){
b[y]=w;
p.push({w,y});
}
}
}
}
inline void insert(ll k,ll b){
q1[++cnt]={k,b};
}
inline ll cal(int x,ll t){
if(!x) return inf;
return q1[x].k*t+q1[x].b;
}
void modify(int x,int l,int r,int t){
if(ver[x]!=cur){
ver[x]=cur;
tr[x]=t;
return;
}
if(l==r){
if(cal(t,l)<cal(tr[x],l)) tr[x]=t;
return;
}
int mid=l+r>>1;
if(cal(t,mid)<cal(tr[x],mid)) swap(tr[x],t);
if(cal(t,l)<cal(tr[x],l)) modify(x*2,l,mid,t);
else if(cal(t,r)<cal(tr[x],r)) modify(x*2+1,mid+1,r,t);
}
ll query(int x,int l,int r,ll t){
if(r<t||l>t||ver[x]!=cur) return inf;
if(l==r) return cal(tr[x],t);
int mid=(l+r)>>1;
ll res=cal(tr[x],t);
if(t<=mid) res=min(res,query(x*2,l,mid,t));
else res=min(res,query(x*2+1,mid+1,r,t));
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m,s,t,x,y;
cin>>n>>m>>s>>t;
for(int i=1;i<=n;i++){
cin>>h[i];
maxn=max(maxn,h[i]);
}
for(int i=1;i<=m;i++){
cin>>x>>y;
q[x].push_back(y);
q[y].push_back(x);
}
dijkstra(s,t);
ans=a[t];
for(int x:q[s]) ans=min(ans,b[x]);
for(int x:q[t]) ans=min(ans,a[x]);
ans*=2;
for(int x=1;x<=n;x++){
cnt=0,cur++;
for(int y:q[x]){
ll k=-2*h[y],b=2*a[y]+h[y]*h[y];
insert(k,b);
modify(1,0,maxn,cnt);
}
for(int y:q[x]){
ll w=query(1,0,maxn,h[y]);
if(w<inf){
w+=2*b[y]+h[y]*h[y];
ans=min(ans,w);
}
}
}
cout<<fixed<<setprecision(6)<<ans/2.0;
return 0;
}