线段树优化连边
有的时候,
对于一个点,向一段区间内所有点连边
边数是(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
", dis[pos[i]] / 2);
return 0;
}