题解 [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)\} \]

人类智慧一下发现函数图像长这样:
image

注意到斜率均为 \(\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;
}
posted @ 2022-08-08 19:53  Administrator-09  阅读(0)  评论(0编辑  收藏  举报