专题:优化建图

线段树优化建图

CF786B Legacy

\(n\) 个点,\(m\) 次连边,求最短路。

  • \(u\) 连向 \(v\)
  • \(u\) 连向 \([l, r]\)
  • \([l, r]\) 连向 \(u\)

操作二:单点向线段连边,且线段存在向下的边走到叶子(绿色边)。

操作三:线段向单点连边,且叶子存在向上的边走到线段(蓝色边)。

一棵树做两个操作会矛盾,开两棵线段树。

\(1\) 父亲连向儿子,操作二即叶子连向树 \(1\) 线段(橙色边)。(叶子不作区分)

\(2\) 儿子连向父亲,操作三即树 \(2\) 线段连向叶子(粉色边)。

两棵树的叶子可视为同一点,对应点间连边权为 \(0\) 的无向边(也可以是 \(1\to2\) 的有向边)。

#include<bits/stdc++.h>
#define eb emplace_back
#define ep emplace
using namespace std;

using ll = long long;
constexpr int N = 2 * 1e5 + 5;

int n, q, s, Leaf[N];
vector<pair<int, int>> G[N << 2];

#define c(x, v) ((x) * 2 + (v))
#define ls x << 1
#define rs ls | 1

void build(int x = 1, int l = 1, int r = n) {
	if(l == r) {
		Leaf[l] = c(x, 0);
		G[c(x, 0)].eb(c(x, 1), 0);
		return;
	}
	int mid = l + r >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	G[c(x, 0)].eb(c(ls, 0), 0), G[c(x, 0)].eb(c(rs, 0), 0);
	G[c(ls, 1)].eb(c(x, 1), 0), G[c(rs, 1)].eb(c(x, 1), 0);
}
void add(int p, int L, int R, int w, int type, int x = 1, int l = 1, int r = n) {
	if(L <= l && r <= R) {
		if(type == 2) {
			G[Leaf[p]].eb(c(x, 0), w);
		}
		else {
			G[c(x, 1)].eb(Leaf[p], w);
		}
		return;
	}
	int mid = l + r >> 1;
	if(L <= mid) add(p, L, R, w, type, ls, l, mid);
	if(R > mid) add(p, L, R, w, type, rs, mid + 1, r);	
}
ll dist[N << 2];
bool st[N << 2];

int main() {
	cin.tie(0)->sync_with_stdio(0);
	cin >> n >> q >> s;
	build();
	for(int i = 1; i <= q; ++ i) {
		int op;
		cin >> op;
		if(op == 1) {
			int u, v, w;
			cin >> u >> v >> w;
			G[Leaf[u]].eb(Leaf[v], w);
		}
		else {
			int u, l, r, w;
			cin >> u >> l >> r >> w;
			add(u, l, r, w, op);
		}
	}
	memset(dist, 0x3f, sizeof dist);
	dist[Leaf[s]] = 0;
	priority_queue<pair<ll, int>> q;
	q.ep(0, Leaf[s]);
	while(!q.empty()) {
		int x = q.top().second;
		q.pop();
		if(st[x]) continue;
		st[x] = 1;
		for(auto [y, z] : G[x]) {
			if(dist[y] > dist[x] + z) {
				dist[y] = dist[x] + z;
				q.ep(-dist[y], y);
			}
		}
	}
	for(int i = 1; i <= n; ++ i) {
		if(dist[Leaf[i]] > 1e18) {
			cout << -1 << ' ';
		}
		else {
			cout << dist[Leaf[i]] << ' '; 
		}
	}
	return 0;
}

P5025 [SNOI2017] 炸弹

点爆一颗炸弹可以点爆所有在其半径内的炸弹。

把第 \(i\) 个炸弹引爆,将引爆多少个炸弹呢?

爆炸范围是连续的。

如果 \(x\) 能引爆 \(y\),则可以用 \(y\) 的左右边界更新 \(x\) 的左右边界。

也就是说可以在缩点后根据反拓扑序求出答案。

只需要单点向区间连边,建一棵下向树足以。

#include<bits/stdc++.h>
#define eb emplace_back
#define ep emplace
using namespace std;

using ll = long long;
constexpr int N = 1e6 + 5, M = N << 2, P = 1e9 + 7;

int n;
struct Bomb{
	ll p, r;
} b[N];

int binary_serch(int i, int l, int r, int type) {
	if(!type) {
		while(l < r) {
			int mid = l + r >> 1;
			if(b[i].p - b[mid].p <= b[i].r) r = mid; 
			else l = mid + 1;
		}
	}
	else {
		while(l < r) {
			int mid = l + r + 1 >> 1;
			if(b[mid].p  - b[i].p <= b[i].r) l = mid; 
			else r = mid - 1;
		}
	}
	return l;
}

#define ls x << 1
#define rs ls | 1

int Leaf[N];
vector<int> G[M];
vector<pair<int, int>> e;

int t[M];

void addedge(int x, int y) {
	G[x].eb(y), e.eb(x, y);
}

void build(int x = 1, int l = 1, int r = n) {
	if(l == r) {
		t[x] = l;
		Leaf[l] = x;
		return;
	}
	int mid = l + r >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
	addedge(x, ls);
	addedge(x, rs); 
}

void add(int p, int L, int R, int x = 1, int l = 1, int r = n) {
	if(L <= l && r <= R) {
		addedge(Leaf[p], x);
		return;
	}
	int mid = l + r >> 1;
	if(mid >= L) add(p, L, R, ls, l, mid);
	if(mid < R) add(p, L, R, rs, mid + 1, r);
}

int dfn[M], low[M], ts, stk[M], ins[M], tp, id[M], L[N], R[N], tot, deg[M];
vector<int> H[M];

void Tarjan(int x) {
	dfn[x] = low[x] = ++ ts;
	ins[stk[++ tp] = x] = 1;
	for(int y : G[x]) {
		if(!dfn[y]) {
			Tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if(ins[y]) {			
			low[x] = min(low[x], dfn[y]);
		}
	}
	if(low[x] == dfn[x]) {
		int y = 0;
		L[++ tot] = n + 1;
		while(y != x) {
			ins[y = stk[tp --]] = 0;
			id[y] = tot;
			if(t[y]) {
				L[tot] = min(L[tot], t[y]);
				R[tot] = max(R[tot], t[y]);
			}
		}
	}
}

int main() {
	cin.tie(0)->sync_with_stdio(0);
	cin >> n;
	for(int i = 1; i <= n; ++ i) {
		cin >> b[i].p >> b[i].r;
	}
	build();
	for(int i = 1; i <= n; ++ i) {
		int l = binary_serch(i, 1, i, 0);
		int r = binary_serch(i, i, n, 1);
		add(i, l, r);
	}
	Tarjan(1);
	for(auto [i, j] : e) {
		if(id[i] != id[j]) {
			H[id[j]].eb(id[i]);
			++ deg[id[i]];
		}
	}
	queue<int> q;
	for(int i = 1; i <= tot; ++ i) {
		if(!deg[i])
			q.push(i);
	}
	while(!q.empty()) {
		int x = q.front();
		q.pop();
		for(int y : H[x]) {
			L[y] = min(L[y], L[x]);
			R[y] = max(R[y], R[x]);
			if(!-- deg[y]) {
				q.push(y);
			}
		}
	}
	ll ans = 0;
	for(int i = 1; i <= n; ++ i) {
		int o = id[Leaf[i]];
		ans = (ans + i * ll(R[o] - L[o] + 1)) % P;
	}
	cout << ans;
	return 0;
}

P6348 [PA2011] Journeys

区间向区间连边,求最短路。

区间与区间连边很容易想到建一个虚点。

对于无向边,需要建两个虚点,否则会建一条自己走到自己的路径,使得该区间任意节点相互到达。

下图为 \([1, 2]\) 连向 \([3, 4]\) 的示例。

#include<bits/stdc++.h>
#define eb emplace_back
#define ep emplace
using namespace std;

using ll = long long;
constexpr int N = 1e6 + 5, M = N << 2;

#define c(x, v) ((x) * 2 + v)
#define ls x << 1
#define rs ls | 1

int n, m, s, Leaf[N], idx;
vector<pair<int, int>> G[M];

void add(int u, int v, int w) {
	G[u].eb(v, w);
}
void build(int x = 1, int l = 1, int r = n) {
	idx = max(idx, c(x, 1));						// 虚点指针
	if(l == r) {
		Leaf[l] = c(x, 0);
		add(c(x, 0), c(x, 1), 0);
		return;
	}
	int mid = l + r >> 1;
	build(ls, l, mid);
	build(rs, mid + 1, r);
	add(c(x, 0), c(ls, 0), 0), add(c(ls, 1), c(x, 1), 0);
	add(c(x, 0), c(rs, 0), 0), add(c(rs, 1), c(x, 1), 0);
}
vector<int> get(int L, int R, int x = 1, int l = 1, int r = n) {
	if(L <= l && r <= R) {
		return vector<int>{x};
	}
	int mid = l + r >> 1;
	if(R <= mid) {
		return get(L, R, ls, l, mid);
	}
	if(L > mid) {
		return get(L, R, rs, mid + 1, r);
	}
	auto a = get(L, R, ls, l, mid);;
	auto b = get(L, R, rs, mid + 1, r);
	if(a.size() < b.size()) {
		swap(a, b);
	}
	for(auto x : b) {
		a.eb(x);
	}
	return a;
}
int d[M], v[M];

int main() {
	cin.tie(0)->sync_with_stdio(0);
	cin >> n >> m >> s;
	build();
	for(int i = 1; i <= m; ++ i) {
		int l, r, L, R;
		cin >> l >> r >> L >> R;
		++ idx;
		for(auto x : get(l, r)) {
			add(c(x, 1), idx, 0), add(idx + 1, c(x, 0), 0);
		}
		for(auto x : get(L, R)) {
			add(idx, c(x, 0), 1), add(c(x, 1), idx + 1, 1);
		}		
		++ idx;
	}
	memset(d, 0x3f, sizeof d);
	d[Leaf[s]] = 0;
	priority_queue<pair<ll, int>> q;
	q.ep(0, Leaf[s]);
	while(!q.empty()) {
		int x = q.top().second;
		q.pop();
		if(v[x]) continue;
		v[x] = 1;
		for(auto [y, z] : G[x]) {
			if(d[y] > d[x] + z) {
				d[y] = d[x] + z;
				q.ep(-d[y], y);
			}
		}
	}
	for(int i = 1; i <= n; ++ i) {
		cout << d[Leaf[i]] << '\n';
	}
	return 0;
}
posted @ 2024-06-15 16:06  Lu_xZ  阅读(7)  评论(0编辑  收藏  举报