题解 [UNR #6] 面基之路
NOI 打成这样不如不去(?)
场上花了 4 个小时过了这题,感觉十分玄幻(?)
第一个结论是那个顺序其实是无效限制
考虑让其他人和 hehe 相遇后就一直跟着 hehe 走
等到所有人都相遇了再光速面基
于是就是找一个点使得到所有出发点(包含 1)距离的最大值最小
发现要么在点上要么在边上,显然边上是更强的条件
发现若在边 \((u, v)\) 上,则最小值可以表示成
\[\min\limits_{x=0}^{w_{u, v}}\{\max\limits_{i=1}^k(\operatorname{dis}(p_i, u)+x, \operatorname{dis}(p_i, v)+w_{u, v}-x)\}
\]
人类智慧一下发现函数图像长这样:
注意到斜率均为 \(\pm 1\)
然后单调栈状物去掉严格不优的子函数
然后将子函数按极值点排序,在相邻极值点间三分出最小值
复杂度 \(O(nk\log n+mk\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, k;
int p[N];
vector<pair<int, int>> to[N];
struct edge{int fir, sec, val;}e[N<<1];
// namespace task1{
// namespace distance{
// bool vis[N];
// priority_queue<pair<ll, int>> q;
// void dijkrstra(int s, ll* dis) {
// memset(vis, 0, sizeof(vis));
// memset(dis, 0x3f, sizeof(dis));
// dis[s]=0; q.push({0, s});
// while (q.size()) {
// int u.q.front().sec; q.pop();
// if (vis[u]) continue;
// vis[u]=1;
// for (auto& v:to[u]) if (dis[v.fir]>dis[u]+v.sec) {
// dis[v.fir]=dis[u]+v.sec;
// q.push({-dis[v.fir], v.fir});
// }
// }
// }
// }
// }
// bool vis[N][21];
// ll dis[21][N], f[N][21];
// priority_queue<pair<ll, pair<int, int>>> q;
// void solve() {
// memset(f, 0x3f, sizeof(f));
// for (int i=1; i<=k; ++i) distance::dijkstra(p[i], dis[i]);
// f[1][0]=0; q.push({1, 0});
// while (q.size()) {
// int u=q.front().fir, i=q.front().sec; q.pop();
// if (vis[u][i]) continue;
// vis[u][i]=1;
// for (auto& v:to[u]) {
// for (int j=1; j<=)
// }
// }
// }
// }
// namespace task1{
// bool vis[N];
// ll dis[22][N], ans=INF;
// priority_queue<pair<ll, int>> q;
// void dijkstra(int s, ll* dis) {
// memset(vis, 0, sizeof(vis));
// for (int i=1; i<=n; ++i) dis[i]=INF;
// dis[s]=0; q.push({0, s});
// while (q.size()) {
// int u=q.top().sec; q.pop();
// if (vis[u]) continue;
// vis[u]=1;
// for (auto& v:to[u]) if (dis[v.fir]>dis[u]+v.sec) {
// dis[v.fir]=dis[u]+v.sec;
// q.push({-dis[v.fir], v.fir});
// }
// }
// }
// void solve() {
// p[++k]=1;
// for (int i=1; i<=k; ++i) dijkstra(p[i], dis[i]);
// for (int i=1; i<=m; ++i) {
// for (int x=0; x<=e[i].val; ++x) {
// ll tem=-INF;
// for (int j=1; j<=k; ++j) tem=max(tem, min(dis[j][e[i].fir]+x, dis[j][e[i].sec]+e[i].val-x));
// // cout<<"tem: "<<tem<<endl;
// ans=min(ans, tem);
// }
// }
// printf("%lld\n", ans);
// }
// }
namespace task2{
int top;
bool vis[N];
ll dis[22][N], ans=INF;
priority_queue<pair<ll, int>> q;
void dijkstra(int s, ll* dis) {
memset(vis, 0, sizeof(vis));
for (int i=1; i<=n; ++i) dis[i]=INF;
dis[s]=0; q.push({0, s});
while (q.size()) {
int u=q.top().sec; q.pop();
if (vis[u]) continue;
vis[u]=1;
for (auto& v:to[u]) if (dis[v.fir]>dis[u]+v.sec) {
dis[v.fir]=dis[u]+v.sec;
q.push({-dis[v.fir], v.fir});
}
}
}
struct line{ll k, b;};
struct func{
line a, b; ll x;
void qlim(ll l, ll r) {
// ll lans=a.k*
// while (l<r) {
// ll lmid=l+(r-l)/3, rmid=r-(r-l)/3;
// lans=a.k*lmid+a.b, rans=b.k*rmid+b.b;
// if (lans>=rans) l=lmid+1;
// else r=rmid-1;
// }
// x=(lans>=rans)?--l:++r;
// assert((b.b-a.b)%(a.k-b.k)==0);
x=min(max(l, (b.b-a.b)/(a.k-b.k)), r);
// ll maxn=-INF, maxi;
// for (int i=l; i<=r; ++i)
// if (min(a.k*i+a.b, b.k*i+b.b)>maxn)
// maxn=min(a.k*i+a.b, b.k*i+b.b), maxi=i;
// x=maxi;
}
ll qval(ll x) {return min(a.k*x+a.b, b.k*x+b.b);}
}f[100], sta[100];
inline bool operator < (func a, func b) {return a.x<b.x;}
ll qmin(func& a, func& b) {
ll l=a.x, r=b.x;
auto f=[&](ll x) {return max(a.qval(x), b.qval(x));};
ll lans=f(l), rans=f(r);
while (l<r) {
ll lmid=l+(r-l)/3, rmid=r-(r-l)/3;
lans=f(lmid), rans=f(rmid);
if (lans<=rans) r=rmid-1;
else l=lmid+1;
}
return min(lans, rans);
// ll ans=INF;
// for (int i=a.x; i<=b.x; ++i) ans=min(ans, max(a.qval(i), b.qval(i)));
// return ans;
}
void solve() {
// cout<<double(sizeof(dis)+sizeof(f)*2)/1000/1000<<endl;
p[++k]=1;
for (int i=1; i<=k; ++i) dijkstra(p[i], dis[i]);
for (int i=1; i<=m; ++i) {
for (int j=1; j<=k; ++j) {
f[j]={{1, dis[j][e[i].fir]}, {-1, dis[j][e[i].sec]+e[i].val}, 0};
f[j].qlim(0, e[i].val);
}
sort(f+1, f+k+1); top=0;
for (int j=1; j<=k; ++j) {
while (top && sta[top].qval(sta[top].x)<=f[j].qval(sta[top].x)) --top;
if (sta[top].qval(f[j].x)<f[j].qval(f[j].x)) sta[++top]=f[j];
}
ans=min(ans, sta[1].qval(0));
ans=min(ans, sta[top].qval(e[i].val));
for (int j=2; j<=top; ++j) ans=min(ans, qmin(sta[j-1], sta[j]));
}
printf("%lld\n", ans);
}
}
signed main()
{
n=read(); m=read();
for (int i=1,u,v,w; i<=m; ++i) {
u=read(); v=read(); w=read()<<1;
to[u].pb({v, w}); to[v].pb({u, w});
e[i]={u, v, w};
}
k=read();
for (int i=1; i<=k; ++i) p[i]=read();
// task1::solve();
task2::solve();
return 0;
}