【洛谷4768】【NOI2018】归程

题意:
一张\(n\)个点\(m\)条边的无向连通图,每条边有两个属性\(w_i\)表示长度,\(a_i\)表示海拔。
每次询问从\(v\)出发回到\(1\),可以先经过一些海拔大于\(p\)的边,此时没有代价,再经过任意边,此时的代价为经过的边权。
问最小代价。

思路:
答案即为所有海拔大于\(p\)的边构成的和\(v\)连通的连通块里面的点到达\(1\)的最短路。
可以先预处理出每个点到\(1\)的最短路径。
然后可以用可持久化并查集维护连通块信息或者建一棵克鲁斯卡尔重构树,直接从\(v\)往上倍增,找到深度最小的合法的一个\(u\),那么答案就是\(u\)这棵子树中的叶子结点点权的最小值。

代码[可持久化并查集]:

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define INF 0x3f3f3f3f
#define N 400010
int n, m, Q, K, S, a[N];
struct Edge {
	int u, v, w, a;
	Edge() {}
	void scan() {
		scanf("%d%d%d%d", &u, &v, &w, &a);
	}
	bool operator < (const Edge &other) const {
		return a > other.a;
	}
}e[N];

struct Graph {
	struct node {
		int to, nx, w;
		node() {}
		node (int to, int nx, int w) : to(to), nx(nx), w(w) {}
	}a[N << 1];
	int head[N], pos;
	void init() {
		for (int i = 1; i <= n; ++i) head[i] = -1;
		pos = 0;  
	}
	void add(int u, int v, int w) {
		a[pos] = node(v, head[u], w); head[u] = pos++;
		a[pos] = node(u, head[v], w); head[v] = pos++;
	}
}G;
#define erp(u) for (int it = G.head[u], v = G.a[it].to, w = G.a[it].w; ~it; it = G.a[it].nx, v = G.a[it].to, w = G.a[it].w)

int dis[N];
struct Dijkstra {
	struct node {
		int u, w;
		node() {}
		node (int u, int w) : u(u), w(w) {}
		bool operator < (const node &other) const {
			return w > other.w;
		}
	};
	bool used[N];
	void work() {
		for (int i = 1; i <= n; ++i) {
			dis[i] = INF;
			used[i] = 0;	
		}
		dis[1] = 0;
		priority_queue <node> pq;
		pq.push(node(1, 0));
		while (!pq.empty()) {
			int u = pq.top().u; pq.pop();
			if (used[u]) continue;
			used[u] = 1;
			erp(u) if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				pq.push(node(v, dis[v]));
			}
		}
	}
}Dij;

struct SEG {
	struct node {
		int ls, rs;
		int fa, sze, val;
		node() {
			ls = rs = 0;
			fa = 0;
			sze = 1;
			val = 0;
		}
	}t[N * 60];
	int rt[N], tot;
	void init() {
		tot = 0;
		rt[0] = 0; t[0] = node();
		for (int i = 1; i <= n; ++i) {
			rt[i] = 0;
		}
	}
	void build(int &id, int l, int r) {
		if (id == 0) {
			id = ++tot;
			t[id] = node();
		}
		if (l == r) {
			t[id].fa = l;  
			t[id].val = dis[l];      
			return;
		}
		int mid = (l + r) >> 1;
		build(t[id].ls, l, mid);
		build(t[id].rs, mid + 1, r);
	}
	void update(int &now, int pre, int l, int r, int pos, int fa, int sze, int val) {
		int tmp = ++tot;
		t[tmp] = node();
		if (l == r) {
			t[tmp].fa = fa;
			t[tmp].sze = sze;
			t[tmp].val = val;
			now = tmp;
			return;
		}
		t[tmp].ls = t[pre].ls;
		t[tmp].rs = t[pre].rs;
		int mid = (l + r) >> 1;
		if (pos <= mid) {
			update(t[tmp].ls, t[pre].ls, l, mid, pos, fa, sze, val);
		} else {
			update(t[tmp].rs, t[pre].rs, mid + 1, r, pos, fa, sze, val);
		}
		now = tmp;
	}
	node query(int now, int l, int r, int pos) {
		if (l == r) {
			return t[now];
		}
		int mid = (l + r) >> 1;
		if (pos <= mid) {
			return query(t[now].ls, l, mid, pos);
		} else {
			return query(t[now].rs, mid + 1, r, pos);
		}
	}	
}seg;

SEG::node find(int k, int x) {
	SEG::node fx = seg.query(seg.rt[k], 1, n, x);
	if (fx.fa == x) {
		return fx;
	} else {
		return find(k, fx.fa);
	}
}

void join(int k, int x, int y) {
	SEG::node fx = find(k, x), fy = find(k, y);
	if (fx.fa != fy.fa) {
		if (fx.sze > fy.sze) swap(fx, fy);  
		seg.update(seg.rt[k], seg.rt[k], 1, n, fx.fa, fy.fa, fx.sze, min(fx.val, fy.val));
		seg.update(seg.rt[k], seg.rt[k], 1, n, fy.fa, fy.fa, fx.sze + fy.sze, min(fx.val, fy.val)); 
		
	}
}

int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		G.init();
		for (int i = 1; i <= m; ++i) {
			e[i].scan();
			G.add(e[i].u, e[i].v, e[i].w);
		}	
		Dij.work();
		sort(e + 1, e + 1 + m);
		for (int i = 1; i <= m; ++i) {
			a[m - i + 1] = e[i].a;
		}
		seg.init(); seg.build(seg.rt[0], 1, n);
		for (int i = 1; i <= m; ++i) {
			seg.rt[i] = seg.rt[i - 1];
			join(i, e[i].u, e[i].v);	
		}
		scanf("%d%d%d", &Q, &K, &S);
		ll lastans = 0, v, p;
		while (Q--) {
			scanf("%lld%lld", &v, &p);
			v = (v + K * lastans - 1) % n + 1;
			p = (p + K * lastans) % (S + 1);
			lastans = dis[v]; 
			int pos = upper_bound(a + 1, a + 1 + m, p) - a;
			if (pos <= m) {
				pos = m - pos + 1;
				SEG::node fv = find(pos, v);
				lastans = fv.val;
			}
			printf("%lld\n", lastans);
		}
	}
	return 0;
}

代码[克鲁斯卡尔重构树]:

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define INF 0x3f3f3f3f
#define N 400010  
int n, m, Q, K, S;
struct Edge {
	int u, v, w, s;
	Edge() {}
	void scan() {
		scanf("%d%d%d%d", &u, &v, &w, &s);
	}
	bool operator < (const Edge &other) const {
		return s > other.s;
	}
}e[N];

struct Graph {
	struct node {
		int to, nx, w, s;
		node() {}
		node (int to, int nx, int w, int s) : to(to), nx(nx), w(w), s(s) {}
	}a[N << 1];
	int head[N], pos;
	void init() {
		for (int i = 1; i <= (n << 1); ++i) head[i] = -1;
		pos = 0;  
	}
	void add(int u, int v, int w, int s = 0) {
		a[pos] = node(v, head[u], w, s); head[u] = pos++;
	}
}G;
#define erp(u) for (int it = G.head[u], v = G.a[it].to, w = G.a[it].w, s = G.a[it].s; ~it; it = G.a[it].nx, v = G.a[it].to, w = G.a[it].w, s = G.a[it].s)

int dis[N];
struct Dijkstra {
	struct node {
		int u, w;
		node() {}
		node (int u, int w) : u(u), w(w) {}
		bool operator < (const node &other) const {
			return w > other.w;
		}
	};
	bool used[N];
	void work() {
		for (int i = 1; i <= n; ++i) {
			dis[i] = INF;
			used[i] = 0;	
		}
		dis[1] = 0;
		priority_queue <node> pq;
		pq.push(node(1, 0));
		while (!pq.empty()) { 
			int u = pq.top().u; pq.pop();
			if (used[u]) continue;
			used[u] = 1;
			erp(u) if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				pq.push(node(v, dis[v]));
			}
		}
	}
}Dij;

struct MST {
	int fa[N << 1], id;
	int find(int u) {
		return fa[u] == u ? u : fa[u] = find(fa[u]);
	}
	void init() {
		for (int i = 1; i < (n << 1); ++i) {
			fa[i] = i;
		}
		id = n;
	}
	void Kruskal() {
		init();
		G.init();
		for (int i = 1; i <= m; ++i) {
			int u = find(e[i].u), v = find(e[i].v);
			if (u == v) continue;
			++id;
			G.add(id, u, e[i].w, e[i].s);
			G.add(id, v, e[i].w, e[i].s);
			fa[u] = fa[v] = id;   
		}
	}
}mst;

int in[N << 1], fin[N << 1], out[N << 1], tim;
int fa[N << 1][20], ss[N << 1][20];
void DFS(int u) {
	if (u <= n) {
		in[u] = ++tim;
		fin[tim] = u;
	} else {
		in[u] = 1e9; 
	}
	for (int i = 1; i < 20; ++i) {
		fa[u][i] = fa[fa[u][i - 1]][i - 1];
		ss[u][i] = min(ss[u][i - 1], ss[fa[u][i - 1]][i - 1]);
	}
	erp(u) {
		fa[v][0] = u;
		ss[v][0] = s;
		DFS(v);
		in[u] = min(in[u], in[v]);
	}
	out[u] = tim; 
}

int Jump(int u, int r) {
	for (int i = 19; ~i; --i) {
		if (fa[u][i] && ss[u][i] > r) {
			u = fa[u][i];
		}
	}
	return u;
}

struct SEG {
	int Min[N << 3];
	void build(int id, int l, int r) {
		Min[id] = 1e9;
		if (l == r) {
			Min[id] = dis[fin[l]];
			return;
		}
		int mid = (l + r) >> 1;
		build(id << 1, l, mid);
		build(id << 1 | 1, mid + 1, r);
		Min[id] = min(Min[id << 1], Min[id << 1 | 1]);
	}
	int query(int id, int l, int r, int ql, int qr) {
		if (l >= ql && r <= qr) {
			return Min[id];
		}
		int mid = (l + r) >> 1;
		int Min = 1e9;
		if (ql <= mid) Min = min(Min, query(id << 1, l, mid, ql, qr));
		if (qr > mid) Min = min(Min, query(id << 1 | 1, mid + 1, r, ql, qr));
		return Min;
	}
}seg;

void init() {
	G.init();
	tim = 0;
	for (int i = 1; i <= (n << 1); ++i) {
		for (int j = 0; j < 20; ++j) {
			fa[i][j] = 0;
			ss[i][j] = 0;
		}
	}
}
int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m); init();
		for (int i = 1; i <= m; ++i) {
			e[i].scan();
			G.add(e[i].u, e[i].v, e[i].w);
			G.add(e[i].v, e[i].u, e[i].w);
		}	
		Dij.work();
		sort(e + 1, e + 1 + m);
		mst.Kruskal();
		DFS(mst.id);
		seg.build(1, 1, n); 
		scanf("%d%d%d", &Q, &K, &S);
		ll lastans = 0, v, p;
		while (Q--) {
			scanf("%lld%lld", &v, &p);
			v = (v + K * lastans - 1) % n + 1;
			p = (p + K * lastans) % (S + 1);
			int pos = Jump(v, p);
			lastans = seg.query(1, 1, n, in[pos], out[pos]);
			printf("%lld\n", lastans);
		}
	}
	return 0;
}
posted @ 2019-07-09 18:56  Dup4  阅读(115)  评论(0)    收藏  举报