【斜率优化】loj_2769「ROI 2017 Day 1」前往大都会

题意

给出\(n\)个点,\(m\)条线路,每条线路上连接\(s_i+1\)个城市(都是有向边),经过每条边需要花费一定时间。
\(1\)去往\(n\),求出在最短时间的前提下使得经过任意两个相邻城市所花费的时间平方和最大(相邻城市指不同线路,也就是换乘)。

思路

\(f_i\)为到达第\(i\)个点的最优答案。
可得\(f_i=f_j+(d_j-d_i)^2,d\)为最短路。

考虑不同线路这一转移条件,可以发现对于同一条线路,当然是越长平方越大,即不会出现\(i\to j\to k\)取代\(i\to k\)的情况(\(i,j,k\)在同一条线路上)。
对于不同的线路,我们不能把它们合在一起,即\(i,j\)要在同一条线路中。

故枚举\(i\)所在的线路,对于每条线路,我们找到一个最优的\(j\)转移。
然后将方程拆开,可以发现用斜率优化,单调栈维护即可。

代码

#pragma GCC optimize(2)
#pragma GCC optimize(Ofast)
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;

const int N = 1005001;

std::vector<std::pair<int, int> > pos[N], rd[N];
std::vector<int> st[N * 2], stid[N];
int n, m, ans, tot, cnt;
int v[N], d[N], X[N], Y[N], f[N], id[N];
int ver[N], next[N], head[N], edge[N];

inline int read() {
	int res = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		res = res * 10 + (c ^ 48), c = getchar();
	return res * f;
}

void add(int u, int v, int w) {
	ver[++tot] = v;
	next[tot] = head[u];
	edge[tot] = w;
	head[u] = tot;
}

void dijkstra() {
	std::priority_queue<std::pair<int, int> > q;
	memset(d, 127 / 3, sizeof(d));
	q.push(std::make_pair(0, 1));
	d[1] = 0;
	while (q.size()) {
		int x = q.top().second, y;
		q.pop();
		if (v[x])
			continue;
		v[x] = 1;
		for (int i = head[x]; i; i = next[i])
			if (d[y = ver[i]] > d[x] + edge[i]) {
				d[y] = d[x] + edge[i];
				q.push(std::make_pair(-d[y], y));
			}
	}	
}

int cmp(int x, int y) {
	return d[x] < d[y];
}

double slope(int x, int y) {
	return (double)(Y[x] - Y[y]) / (X[x] - X[y]);
}

void dp() {
	for (int i = 1; i <= n; i++)
		id[i] = i;
	std::sort(id + 1, id + n + 1, cmp);
	for (int i = 1; i <= n; i++) {
		int u = id[i];
		for (int j = 0; j < pos[u].size(); j++) {
			int x = pos[u][j].first, y = pos[u][j].second;
			if (y && d[rd[x][y - 1].first] + rd[x][y - 1].second == d[u])
				stid[x][y] = stid[x][y - 1];
			else
				stid[x][y] = ++cnt;
			int p = stid[x][y];
			while (st[p].size() >= 2 && slope(st[p][st[p].size() - 2], st[p][st[p].size() - 1]) < d[u])
				st[p].pop_back();
			if (st[p].size()) {
				int y = st[p].back();
				f[u] = std::max(f[u], f[y] + (d[u] - d[y]) * (d[u] - d[y]));
			}
		}
		X[u] = 2 * d[u], Y[u] = f[u] + d[u] * d[u];
		for (int j = 0; j < pos[u].size(); j++) {
			int x = pos[u][j].first, y = pos[u][j].second, p = stid[x][y];
			while (st[p].size() >= 2 && slope(st[p][st[p].size() - 2], st[p][st[p].size() - 1]) < slope(st[p][st[p].size() - 1], u))
				st[p].pop_back();
			st[p].push_back(u);
		}
	}
}

signed main() {
	n = read(), m = read();
	for (int i = 1, s, x, w, y; i <= m; i++) {
		s = read(), x = read();
		pos[x].push_back(std::make_pair(i, 0));
		stid[i].push_back(0);
		for (int j = 1; j <= s; j++) {
			w = read(), y = read();
			pos[y].push_back(std::make_pair(i, j));
			rd[i].push_back(std::make_pair(x, w));
			stid[i].push_back(0);
			add(x, y, w);
			x = y;
		}
		rd[i].push_back(std::make_pair(x, 0));
	}
	dijkstra();
	dp();
	printf("%lld %lld", d[n], f[n]);
}
posted @ 2021-08-08 15:34  nymph181  阅读(160)  评论(0)    收藏  举报