QOJ
题解
]
QOJ5540 City Hall 题解
首先,对 $s,t$ 各跑出单源最短路,然后考虑枚举修改点点 $i$,那么就需要对于路径 $s\to i\to t$ 求值。
枚举 $i$ 的相邻点 $u,v$,那么不难证明要把 $h_i$ 设置为 $\frac12(h_u+h_v)$,那么答案就是
展开括号
\[dis_{s\to u}+dis_{t\to v}+\frac12h_u^2+\frac12h_v^2-h_uh_v\]我们不能对每个点所有的相邻点进行枚举,会被菊花卡到超时,考虑快速找点对。
发现这是一条线段,可以使用李超树来维护。其中线段的斜率是 $-h_u$,截距是 $dis_{s\to u}+\frac12h_u^2$,查询就是找 $x=h_v$ 时的最大值,然后再加上 $dis_{t\to v}+\frac12h_v^2$。
但是,如果我们对斜率 $h$ 进行排序,会发现这是凸壳求 $\max$,并且不在线,可以使用整体二分。
注意:使用整体二分求凸壳 $\max$ 时,实际上相当于钦定了先来的点是 $u$ 后来的点是 $v$,所以贡献就是 $dis_{s\to u}+dis_{t\to v}$ 而不是 $\min(dis_{s\to u}+dis_{t\to v},dis_{s\to v}+dis_{t\to u})$,取 $\min$ 会破坏凸壳导致 WA。
另外,需要特判 $u,v$ 被修改的情况。
另外,最后不能直接把答案除以二,会掉精度,
#include<bits/stdc++.h>
#define siz(x) int((x).size())
#define all(x) std::begin(x),std::end(x)
#define fi first
#define se second
using namespace std;
using unt=unsigned;
using loli=long long;
using lolu=unsigned long long;
using pii=pair<int,int>;
using pli=pair<loli,int>;
mt19937_64 rng(random_device{}());
constexpr int N=1e5+7;
constexpr loli inf=1e18;
int n,m,s1,s2;
int h[N],vis[N];
loli d[2][N],ans=inf;
basic_string<int>g[N];
priority_queue<pli>q;
loli sqr(int x){return 1ll*x*x;}
void dijk1(int s,loli *dis){
memset(vis+1,0,sizeof(int)*n);
for(int i=1;i<=n;i++)dis[i]=inf;
q.emplace(dis[s]=0,s);
while(!q.empty()){
int u=q.top().se;q.pop();
if(vis[u])continue;else vis[u]=1;
for(int v:g[u]){
loli w=sqr(h[u]-h[v]);
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v])q.emplace(-dis[v],v);
}
}
}
}
void dijk2(int s,loli *dis){
memset(vis+1,0,sizeof(int)*n);
for(int i=1;i<=n;i++)dis[i]=inf;
q.emplace(dis[s]=0,s);
for(int v:g[s])q.emplace(dis[v]=0,v);
while(!q.empty()){
int u=q.top().se;q.pop();
if(vis[u])continue;else vis[u]=1;
for(int v:g[u]){
loli w=sqr(h[u]-h[v]);
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v])q.emplace(-dis[v],v);
}
}
}
}
#define mid ((l+r)>>1)
void solve(int u,int l,int r,int x,int y){
if(l>r)return;
int pos=-1;loli tmp=8*inf;
for(int k=x;k<=y;k++){
int p=g[u][mid],q=g[u][k];
loli sum=2*(d[0][p]+d[1][q])+sqr(h[p])+sqr(h[q])-2ll*h[p]*h[q];
if(tmp>sum)tmp=sum,pos=k;
}
ans=min(ans,tmp);
solve(u,l,mid-1,x,pos);
solve(u,mid+1,r,pos,y);
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(false);cin.tie(nullptr);
cin>>n>>m>>s1>>s2;
for(int i=1;i<=n;i++)cin>>h[i];
for(int i=1,u,v;i<=m;i++)cin>>u>>v,g[u]+=v,g[v]+=u;
dijk2(s1,d[0]);ans=min(ans,2*d[0][s2]);
dijk2(s2,d[1]);ans=min(ans,2*d[1][s1]);
dijk1(s1,d[0]);
dijk1(s2,d[1]);
for(int i=1;i<=n;i++){
sort(all(g[i]),[](int x,int y){
return h[x]<h[y];
});
solve(i,0,siz(g[i])-1,0,siz(g[i])-1);
}
cout<<ans/2;
if(ans&1)cout<<".5";
return 0;
}