[NOI2018]归程(kruscal重构树+最短路+树上倍增)

NOI的模板题?貌似2018已经有两道模板了啊?

我们现在想要找到一个点集,使得从起点到点集中的每个点一定存在一条路径使得这条路径上的最小边大于水位线。

这就要使得我们从起点找到一条到每个点的路径使得路径上的最小边尽量打。这不就是kruskal重构树能干的事吗?不会的点.

我们在重构树上倍增,找到一个深度最浅的节点使得其权值大于水位线,则其子树内的叶节点都是可以到的。

剩下的交给步行,我们发现终点是固定的所以步行的最短路是固定的,直接spfa搞出一个最短路再树形dp统计一下就行。

时间复杂度是\(n\log{n}\)级别的。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int N = 400010;
const int M = 400010;
template <typename T> void read(T &x) {
	T ff = 1;
	char cch = getchar();
	for (; '0' > cch || cch > '9'; cch = getchar()) if (cch == '-') ff = -1;
	for (x = 0; '0' <= cch && cch <= '9'; cch = getchar()) x = x * 10 + cch - '0';
	x *= ff;
}
struct node{
	int pre, to, val;
}edge[N << 1];
struct EDGE{
	int u, v, l, w;
	friend bool operator < (EDGE x, EDGE y) {
		return x.w > y.w;
	}
}ed[M];
int head[N], tot;
int fa[N], val[N];
int ch[N][2], dp[N], dis[N];
bool vis[N];
int f[N][21];
int T;
int n, m, cnt;
int Q, k, s;
int ans;
priority_queue<pair<int, int> > q;
void add(int u, int v, int l) {
	edge[++tot] = node{head[u], v, l};
	head[u] = tot;
}
void init() {
	tot = 0;
	for (int i = 1; i <= n; i++) head[i] = 0;
}
void dfs(int x) {
	dp[x] = 0x3f3f3f3f;
	if (val[x] >= 0x3f3f3f3f) dp[x] = dis[x];
	for (int i = 1; i <= 20; i++) {
		f[x][i] = f[f[x][i - 1]][i - 1];
	}
	if (ch[x][0]) {
		f[ch[x][0]][0] = x;
		dfs(ch[x][0]);
		dp[x] = min(dp[x], dp[ch[x][0]]);
	}
	if (ch[x][1]) {
		f[ch[x][1]][0] = x;
		dfs(ch[x][1]);
		dp[x] = min(dp[x], dp[ch[x][1]]);
	}
}
int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int main() {
	read(T);
	while (T--) {
		read(n); read(m);
		init();
		for (int i = 1; i <= m; i++) {
			read(ed[i].u); read(ed[i].v); read(ed[i].l); read(ed[i].w);
			add(ed[i].u, ed[i].v, ed[i].l);
			add(ed[i].v, ed[i].u, ed[i].l);
		}
		for (int i = 1; i <= n; i++) dis[i] = 0x3f3f3f3f, vis[i] = 0;
		dis[1] = 0;
		q.push(make_pair(-dis[1], 1));
		while (!q.empty()) {
			int x = q.top().second;
			q.pop();
			if (vis[x]) continue;
			vis[x] = 1;
			for (int i = head[x]; i; i = edge[i].pre) {
				int y = edge[i].to;
				if (dis[y] > dis[x] + edge[i].val) {
					dis[y] = dis[x] + edge[i].val;
					q.push(make_pair(-dis[y], y));
				}
			}
		}
		int limit = (n << 1) - 1;
		cnt = n;
		for (int i = 1; i <= limit; i++) fa[i] = i;
		sort(ed + 1, ed + 1 + m);
		for (int i = 1; i <= n; i++) val[i] = 0x3f3f3f3f, ch[i][0] = ch[i][1] = 0;
		for (int i = 1; i <= m; i++) {
			int x = ed[i].u, y = ed[i].v;
			int fx = find(x), fy = find(y);
			if (fx != fy) {
				cnt++;
				val[cnt] = ed[i].w;
				fa[fx] = cnt;
				fa[fy] = cnt;
				ch[cnt][0] = fx;
				ch[cnt][1] = fy;
				if (cnt >= limit) break;
			}
		}
		f[cnt][0] = 0;
		dfs(cnt);
		read(Q); read(k); read(s);
		ans = 0;
		for (int t = 1; t <= Q; t++) {
			int v, p;
			read(v); read(p);
			v = (v + k * ans - 1) % n + 1;
			p = (p + k * ans) % (s + 1);
			for (int i = 20; i >= 0; i--) {
				if (val[f[v][i]] > p) v = f[v][i];
			}
			printf("%d\n", ans = dp[v]);
		}
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/zcr-blog/p/13394035.html