线段树优化连边

有的时候,

对于一个点,向一段区间内所有点连边

边数是\(O(n^2)\)

复杂度爆炸

于是就有了线段树优化连边......

线段树优化连边,利用到线段树的思想.

对于每个区间,新建一个节点,向子区间递归连边.

这样,当连向一段区间,就等于连向了所有其子节点

十分抽象.

看几道例题

[luogu 5025] [SNOI2017]炸弹

题目链接

题解

暴力做法很显然

连完边后跑\(tarjan + DAG dp\)

然后,线段树优化连边

Code

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
	x = 0; RG char c = getchar(); bool f = 0;
	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
	x = f ? -x : x;
	return ;
}
template<class T> inline void write(T x) {
	if (!x) {putchar(48);return ;}
	if (x < 0) x = -x, putchar('-');
	int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
	for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 500010, M = 20000010, Mod = 1000000007;

int n, num[N << 2], isleaf[N << 2], tot;

#define ls (rt << 1)
#define rs (rt << 1 | 1)

struct node {
	int to, nxt;
}g[M];
int last[N << 2], gl, L[N], R[N];
void add(int x, int y) {
	g[++gl] = (node) {y, last[x]};
	last[x] = gl;
	return ;
}

struct Segment_Tree {	
	void build(int rt, int l, int r) {
		tot = max(tot, rt);
		if (l == r) {
			isleaf[rt] = 1;
			num[l] = rt;
			return ;
		}
		int mid = (l + r) >> 1;
		add(rt, ls); add(rt, rs);
		build(ls, l, mid), build(rs, mid + 1, r);
		return ;
	}
	void link(int rt, int l, int r, int L, int R, int id) {
		if (L <= l && r <= R) {
			add(id, rt);
			return ;
		}
		int mid = (l + r) >> 1;
		if (L <= mid) link(ls, l, mid, L, R, id);
		if (R > mid) link(rs, mid + 1, r, L, R, id);
		return ;
	}
}T;

LL x[N], y[N];
int dfn[N << 2], low[N << 2], cnt, color[N << 2], C, val[N << 2];
bool in[N << 2];

stack<int> s;

void tarjan(int u) {
	dfn[u] = low[u] = ++cnt;
	in[u] = 1;
	s.push(u);
	for (int i = last[u]; i; i = g[i].nxt) {
		int v = g[i].to;
		if (!dfn[v]) {
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (in[v]) low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u]) {
		C++;
		while (in[u]) {
			color[s.top()] = C;
			val[C] += isleaf[s.top()];
			in[s.top()] = 0;
			s.pop();
		}
	}
	return ;
}

int dp[N << 2];

vector<int> G[N << 2];

inline int dfs(int u) {
	if (dp[u]) return dp[u];
	dp[u] = val[u];
	for (int i = 0; i < (int) G[u].size(); i++) {
		int v = G[u][i];
		dp[u] += dfs(v);
	}
	return dp[u];
}

int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(x[i]), read(y[i]);
	for (int i = 1; i <= n; i++) {
		L[i] = lower_bound(x + 1, x + 1 + n, x[i] - y[i]) - x;
		R[i] = upper_bound(x + 1, x + 1 + n, x[i] + y[i]) - x - 1;
	}
	T.build(1, 1, n);
	for (int i = 1; i <= n; i++)
		T.link(1, 1, n, L[i], R[i], num[i]);
	for (int i = 1; i <= tot; i++)
		if (!dfn[i]) tarjan(i);
	for (int i = 1; i <= tot; i++)
		for (int j = last[i]; j; j = g[j].nxt) {
			int v = g[j].to;
			if (color[i] == color[v]) continue;
			G[color[i]].push_back(color[v]);
		}
	for (int i = 1; i <= C; i++) {
		sort(G[i].begin(), G[i].end());
		G[i].erase(unique(G[i].begin(), G[i].end()), G[i].end());
	}
	for (int i = 1; i <= C; i++)
		if (!dp[i])
			dfs(i);
	LL ans = 0;
	for (int i = 1; i <= n; i++)
		(ans += 1ll * i * dp[color[num[i]]]) %= Mod;
	write(ans);
	return 0;
}

看完这题后,应该对于线段树优化连边有了个大概..

然后看一道稍微难一点的.

BZOJ3073 Journeys

权限题警告

题目描述

Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路。N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a,b),(c,d)表示,对于任意两个国家x,y,如果a<=x<=b,c<=y<=d,那么在xy之间建造一条道路。Seter保证一条道路不会修建两次,也保证不会有一个国家与自己之间有道路。 Seter好不容易建好了所有道路,他现在在位于P号的首都。Seter想知道P号国家到任意一个国家最少需要经过几条道路。当然,Seter保证P号国家能到任意一个国家。

注意:可能有重边

题解

这回有两个区间了怎么办?

不能对于(a,b)每个点连区间吧..

我们弄两棵线段树

对于每个点,一棵树管出边,另一棵管入边

线段树里面的边长度都为\(0\)

两棵线段树对应点之间连\(0\)的边

至于为什么要两棵线段树,

想想第一题,我们线段树之中的边只能控制一个方向(如果双向边,那两点距离不就变成\(0\)了)?

因为第一题是点连向区间,所以只要一个线段树来控制区间

然而,这题是区间连向区间

所以我们需要两棵线段树

然后考虑加边,

因为两个区间都被分成了很多段

我们不能每段两两连边

那么我们可以新建一个节点来整合区间

具体实现看代码

Code

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
	x = 0; RG char c = getchar(); bool f = 0;
	while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
	while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
	x = f ? -x : x;
	return ;
}
template<class T> inline void write(T x) {
	if (!x) {putchar(48);return ;}
	if (x < 0) x = -x, putchar('-');
	int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
	for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

int n, m, p, tot;

const int N = 2000010;

struct node {
	int ls, rs;
}t[N << 2];

struct Graph {
	int to, nxt, w;
}g[N * 5];
int last[N << 1], gl, pos[N];
inline void add(int x, int y, int z) {
	g[++gl] = (Graph) {y, last[x], z};
	last[x] = gl;	
}

inline void build1(int &rt, int l, int r) {
	rt = ++tot;
	if (l == r) {
		pos[l] = rt;
		return ;
	}
	int mid = (l + r) >> 1;
	build1(t[rt].ls, l, mid), build1(t[rt].rs, mid + 1, r);
	add(t[rt].ls, rt, 0), add(t[rt].rs, rt, 0);
	return ;
}

inline void build2(int &rt, int l, int r) {
	rt = ++tot;
	if (l == r) {
		add(rt, pos[l], 0);
		return ;
	}
	int mid = (l + r) >> 1;
	build2(t[rt].ls, l, mid), build2(t[rt].rs, mid + 1, r);
	add(rt, t[rt].ls, 0), add(rt, t[rt].rs, 0);
	return ;
}

inline void link1(int rt, int l, int r, int L, int R) {
	if (L <= l && r <= R) {
		add(rt, tot, 1);
		return ;
	}
	int mid = (l + r) >> 1;
	if (L <= mid)
		link1(t[rt].ls, l, mid, L, R);
	if (R > mid)
		link1(t[rt].rs, mid + 1, r, L, R);
	return ;
}

inline void link2(int rt, int l, int r, int L, int R) {
	if (L <= l && r <= R) {
		add(tot, rt, 1);
		return ;
	}
	int mid = (l + r) >> 1;
	if (L <= mid)
		link2(t[rt].ls, l, mid, L, R);
	if (R > mid)
		link2(t[rt].rs, mid + 1, r, L, R);
	return ;
}

priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > >q;

int dis[N << 1];
bool vis[N << 1];

void dijkstra() {
	memset(dis, 127/3, sizeof(dis));
	dis[pos[p]] = 0;
	q.push(make_pair(0, pos[p]));
	while (!q.empty()) {
		while (vis[q.top().second] && !q.empty()) q.pop();
		if (q.empty()) break;
		int u = q.top().second; q.pop();
		vis[u] = 1;
		for (RG int i = last[u]; i; i = g[i].nxt) {
			int v = g[i].to;
			if (dis[v] > dis[u] + g[i].w) {
				dis[v] = dis[u] + g[i].w;
				q.push(make_pair(dis[v], v));
			}
		}
	}
	return ;
}

int main() {
	read(n), read(m), read(p);
	int rt1, rt2;
	rt1 = rt2 = 0;
	build1(rt1, 1, n); build2(rt2, 1, n);
	while (m--) {
		int a, b, c, d;
		read(a), read(b), read(c), read(d);
		++tot; link1(rt1, 1, n, a, b); link2(rt2, 1, n, c, d);
		++tot; link1(rt1, 1, n, c, d); link2(rt2, 1, n, a, b);
	}
	dijkstra();
	for (int i = 1; i <= n; i++)
		printf("%d\n", dis[pos[i]] / 2);
	return 0;
}

posted @ 2019-03-04 21:43  zzy2005  阅读(448)  评论(0编辑  收藏  举报