(线段树) 优化建图

图论中形如点向一段区间连边,(或者区间向区间?)的问题。

板题

题意:点向点连边,点向区间连边,区间向点连边,求最短路。

运用区间划分思想,开两颗线段树,一颗存第二种边,一颗存第三种边(注意不能混用!!)

线段树上,区间和所管的子区间连向上或向下的边,边权为 0。

然后原图中每个点对应的就是线段树上的叶子节点,所以线段树间对应的叶子也要连边权为 0 的边。

这样每种边就被拆成了 log 个区间,类似线段树区间修改的方式插入即可。

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using db = double;
using ull = unsigned long long;
using ldb = long double;
using pii = pair<ll, int>;

#define pb push_back
#define pc putchar
#define sp pc(' ')
#define et pc('\n')
#define debug cerr<<"--ERROR--\n"
#define rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define per(i, a, b) for(int i = (a); i >= (b); --i)
#define mem(a, x) memset(a, x, sizeof(a))
#define in(a, n) rep(i, 1, n) a[i]=rd()
#define all(x) x.begin(), x.end()

inline ll rd(){ll x=0; int f=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-48; c=getchar();} return x*f;}
inline void wr(ll x){if(x<0) putchar('-'), x=-x; static int stk[35]; int tp=0; do stk[tp++]=x%10;while(x/=10); do pc(stk[--tp]^48);while(tp);}

//const ldb eps = 1e-9;
const ll INF = 0x3f3f3f3f3f3f3f3f;
//const int mo = 1e9+7;
const int N = 1e6+3;
const int B = 5e5;

int n, q, S, pos[N], vis[N];
ll dis[N];
vector <pii> g[N];

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

void clear() {  }
void add(int u, int v, int w) {
//	printf("%d %d %d\n", u, v, w);
	g[u].pb({v, w});
}
void build(int p, int l, int r) {
	if(l == r) {
		pos[l] = p;
		add(p, p+B, 0), add(p+B, p, 0);
		return ;
	}
	add(p, ls(p), 0), add(p, rs(p), 0);
	add(ls(p)+B, p+B, 0), add(rs(p)+B, p+B, 0);
	int mid = l+r>>1;
	build(ls(p), l, mid), build(rs(p), mid+1, r);
}
void upd(int p, int l, int r, int x, int y, int u, int w, int op) {
	// 1 : [x, y] -> u, 0 : u -> [x, y]
	if(l >= x && r <= y) {
		if(op) add(p+B, pos[u]+B, w);
		else add(pos[u], p, w);
		return ;
	}
	int mid = l+r>>1;
	if(x <= mid) upd(ls(p), l, mid, x, y, u, w, op);
	if(y > mid) upd(rs(p), mid+1, r, x, y, u, w, op);
}
void dij(int s) {
	mem(dis, INF);
	dis[s] = 0;
	priority_queue <pii> q;
	q.push({0, s});
	while(!q.empty()) {
		int u = q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		for(auto t : g[u]) {
			int v = t.first, w = t.second;
			if(dis[u]+w < dis[v]) dis[v] = dis[u]+w, q.push({-dis[v], v});
		}
	}
}
signed main() {
//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);

int TT = 1;
//TT = rd();
while(TT--)
{
	clear();
	cin >> n >> q >> S;
	build(1, 1, n);
	while(q--) {
		int op=rd(), x=rd(), y=rd(), z=rd();
		if(op == 1) {
			add(pos[x], pos[y], z);
		}
		else {
			upd(1, 1, n, y, z, x, rd(), op&1);
		}
	}
	dij(pos[S]);
	rep(i, 1, n) wr(dis[pos[i]]==INF ? -1 : dis[pos[i]]), sp;
    et;
}
    return 0;
}

前后缀、倍增优化等同理

posted @ 2024-11-22 22:37  lowbit  阅读(60)  评论(0)    收藏  举报