「学习笔记」线段树优化建图
线段树优化建图适用于对一段区间内的点进行连边,如果暴力连边,复杂度是 \(O(n^2m)\) 的,显然过大。
考虑支持各种区间操作的线段树。建立两棵线段树,第一棵树从父亲向儿子连边权为 0 的边,这里称为入树,第二棵树从儿子向父亲连边权为 0 的边这里称为出树,它们的叶子节点是相同的,为题目里给出的 \(1 - n\) ,这样区间连边就可以连到父亲上了。
题意:
\([a,b]\) 内的点向 \([c,d]\) 内的点连边,边权为1。
求 \(s\) 到 \(1-n\) 的最短路。
连边是新开两个节点 \(u\) 和 \(v\),出树里 \([a,b]\) 所对应的点向 \(v\) 连边,边权为0,\(v\) 到 \(u\) 连边,边权为1,\(u\) 向入树里 \([c,d]\) 所对应的点连边,边权为0。
然后跑最短路即可。
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int, int> P;
const int N = 2e6 + 5;
int rd()
{
int x = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
return x;
}
int rt1, rt2, cnt, lc[N], rc[N];
vector <P> G[N];
void add(int u, int v, int w)
{
G[u].push_back(P(v, w));
}
void build(int &p1, int &p2, int l, int r)
{
if(l == r)
{
p1 = p2 = l;
return;
}
p1 = ++cnt, p2 = ++cnt;
int mid = (l + r) >> 1;
build(lc[p1], lc[p2], l, mid);
add(p1, lc[p1], 0), add(lc[p2], p2, 0);
build(rc[p1], rc[p2], mid + 1, r);
add(p1, rc[p1], 0), add(rc[p2], p2, 0);
}
void update(int p, int l, int r, int L, int R, int u, int op)
{
if(L <= l && r <= R)
{
if(op) add(u, p, 0);
else add(p, u, 0);
return;
}
int mid = (l + r) >> 1;
if(L <= mid) update(lc[p], l, mid, L, R, u, op);
if(R > mid) update(rc[p], mid + 1, r, L, R, u, op);
}
int dis[N];
priority_queue <P, vector<P>, greater<P> > que;
void dijkstra(int s)
{
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
que.push(P(0, s));
while(!que.empty())
{
P p = que.top();
que.pop();
int u = p.second;
if(dis[u] < p.first) continue;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i].first, w = G[u][i].second;
if(dis[u] + w < dis[v])
{
dis[v] = dis[u] + w;
que.push(P(dis[v], v));
}
}
}
}
int main()
{
int n = rd(), m = rd(), s = rd();
cnt = n;
build(rt1, rt2, 1, n);
while(m--)
{
int a = rd(), b = rd(), c = rd(), d = rd(), u, v;
u = ++cnt, v = ++cnt;
add(v, u, 1);
update(rt1, 1, n, a, b, u, 1);
update(rt2, 1, n, c, d, v, 0);
u = ++cnt, v = ++cnt;
add(v, u, 1);
update(rt1, 1, n, c, d, u, 1);
update(rt2, 1, n, a, b, v, 0);
}
dijkstra(s);
for(int i = 1; i <= n; i++)
printf("%d\n", dis[i]);
return 0;
}
题意:
- \(u\) 向 \(v\) 连边,边权为 \(w\)。
- \(u\) 向 \([l,r]\) 连边,边权为 \(w\)。
- \([l,r]\) 向 \(u\) 连边,边权为 \(w\)。
求 \(s\) 到 \(1-n\) 的最短路。
其实是一样的,就是本题可以直接用 \(u\) 连边,好写一些。
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#define int long long
using namespace std;
typedef pair<int, int> P;
const int N = 1e6 + 5;
int n, m, s;
vector <P> G[N];
int cnt, rt1, rt2, ls[N], rs[N];
void buildOUT(int l, int r, int &rt)
{
if(l == r)
{
rt = l;
return;
}
rt = ++cnt;
int mid = (l + r) >> 1;
buildOUT(l, mid, ls[rt]);
buildOUT(mid + 1, r, rs[rt]);
G[rt].push_back(P(ls[rt], 0));
G[rt].push_back(P(rs[rt], 0));
}
void buildIN(int l, int r, int &rt)
{
if(l == r)
{
rt = l;
return;
}
rt = ++cnt;
int mid = (l + r) >> 1;
buildIN(l, mid, ls[rt]);
buildIN(mid + 1, r, rs[rt]);
G[ls[rt]].push_back(P(rt, 0));
G[rs[rt]].push_back(P(rt, 0));
}
void update(int rt, int l, int r, int u, int L, int R, int w, int op)
{
if(L <= l && r <= R)
{
if(op == 2) G[u].push_back(P(rt, w));
else G[rt].push_back(P(u, w));
return;
}
int mid = (l + r) >> 1;
if(L <= mid) update(ls[rt], l, mid, u, L, R, w, op);
if(R > mid) update(rs[rt], mid + 1, r, u, L, R, w, op);
}
int dis[N];
priority_queue <P, vector<P>, greater<P> > que;
void dijkstra(int s)
{
for(int i = 1; i <= cnt; i++) dis[i] = 1e18;
dis[s] = 0;
que.push(P(0, s));
while(!que.empty())
{
P p = que.top();
que.pop();
int u = p.second;
if(dis[u] < p.first) continue;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i].first, w = G[u][i].second;
if(dis[u] + w < dis[v])
{
dis[v] = dis[u] + w;
que.push(P(dis[v], v));
}
}
}
}
int read()
{
int x = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
return x;
}
signed main()
{
n = read(), m = read(), s = read();
cnt = n;
buildOUT(1, n, rt1), buildIN(1, n, rt2);
while(m--)
{
int op = read();
if(op == 1)
{
int u = read(), v = read(), w = read();
G[u].push_back(P(v, w));
}
else
{
int u = read(), l = read(), r = read(), w = read();
update(op == 2 ? rt1 : rt2, 1, n, u, l, r, w, op);
}
}
dijkstra(s);
for(int i = 1; i <= n; i++)
printf("%lld ", dis[i] == 1e18 ? -1 : dis[i]);
return 0;
}
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$