传送门
题意: 有n个点, q次操作, 操作有三种:
1 u v w 表示u -> v 有有一条权值为w的边.
2 u l r w 表示 u 到 下标在[l, r] 的城市 有一条权值为w的边
3 u l r w 表示 下标在[l, r] 的城市 到 u 有一条权值为w的边
然后求给出起点s, 输出s到每一个点的最小花费. 不能到达输出-1.
思路: 很明显直接暴力建边, 建的过程就会T, 边数也非常多. 但是我们可以发现连的城市是一段连续区间, 所以我们需要利用这个特性, 那么就很明显想到线段树区间分层问题. 这样我们的建图的边数最多只有nlogn条边, 建边的过程也优化了, 具体操作过程为: 我们需要两颗线段树. 第一颗叫入树, 第二颗叫出树, 入树的每段区间都向左右儿子连一条权值为0的边, 叶子节点向相应点连一条权值为0的边, 然后出树相反, 是左右儿子向父节点连一条权值为0的边, 相应点向出树的叶子节点连一条权值为0的边. 都是有向边.
然后对于操作, 1操作直接连点加权边, 2操作u 向 入树对应区间的所有标号连一条权值为w的有向边, 3操作 出树的所有区间标号向u连一条权值为w的有向边. 这样就优化完成了, 并且符合要求, 画出来就很明显可以看出来了….
那么点边这么多, 肯定用优化的dij跑最短路呀. 注意点数和边数, 尽量大一点.
AC Code
const int maxn = 6e5 + 5;
const int maxm = 3e6 + 5;
struct node {
int to, next; ll w;
bool operator < (const node &_) const {
return w > _.w;
}
}e[maxm];
int n, q, s, idx;
int head[maxn], cnt;
struct Tree {
int tl, tr, val;
}tree[2][maxn];
void init() {
Fill(head, -1); cnt = 0;
Fill(tree, 0); idx = n;
}
void add(int u, int v, int w) {
e[cnt] = node{v, head[u], w};
head[u] = cnt++;
}
void build(int id, int l, int r, int f) {
tree[f][id].val = ++idx;
tree[f][id].tl = l, tree[f][id].tr = r;
if (l == r) {
if (!f) add(idx, l, 0);
else add(l, idx, 0);
return ;
}
int mid = (l + r) >> 1;
build(id<<1, l, mid, f);
build(id<<1|1, mid+1, r, f);
if (!f) {
add(tree[f][id].val, tree[f][id<<1].val, 0);
add(tree[f][id].val, tree[f][id<<1|1].val, 0);
}
else {
add(tree[f][id<<1].val, tree[f][id].val, 0);
add(tree[f][id<<1|1].val, tree[f][id].val, 0);
}
}
vector<int>vs;
void query(int id, int ql, int qr, int f) {
int l = tree[f][id].tl, r = tree[f][id].tr;
if (ql <= l && r <= qr) {
vs.pb(tree[f][id].val);
return ;
}
int mid = (l + r) >> 1;
if (qr <= mid) query(id<<1, ql, qr,f );
else if (ql > mid) query(id<<1|1, ql, qr, f);
else {
query(id<<1, ql, mid, f);
query(id<<1|1, mid+1, qr, f);
}
}
ll dis[maxn]; bool vis[maxn];
void dij(int st,int ed) {
priority_queue q;
for (int i = 1 ; i <= maxn-5 ; i ++) {
dis[i] = INF;
vis[i] = 0;
}
dis[st] = 0;
q.push(node{st, 0, 0});
while (!q.empty()) {
node u = q.top();
q.pop();
if(vis[u.to]) continue;
vis[u.to] = 1;
for (int i = head[u.to]; ~i; i = e[i].next) {
int to = e[i].to;
if (dis[to] > dis[u.to] + e[i].w) {
dis[to] = dis[u.to] + e[i].w;
q.push(node{to, 0, dis[to]});
}
}
}
}
void solve() {
while(~scanf("%d%d%d", &n, &q, &s)) {
init();
build(1, 1, n, 0);
build(1, 1, n, 1);
while(q--) {
int op, u, v, l, r, w;
scanf("%d%d", &op, &u);
if (op == 1) {
scanf("%d%d", &v, &w);
add(u, v, w);
}
else if (op == 2) {
scanf("%d%d%d", &l, &r, &w);
vs.clear();
query(1, l, r, 0);
for (int i = 0 ; i < sz(vs) ; i ++) {
add(u, vs[i], w);
}
}
else {
scanf("%d%d%d", &l, &r, &w);
vs.clear();
query(1, l, r, 1);
for (int i = 0 ; i < sz(vs) ; i ++) {
add(vs[i], u, w);
}
}
}
dij(s, n);
for (int i = 1 ; i <= n ; i ++) {
printf("%lld%c", dis[i] == INF ? -1ll : dis[i], i == n ?'\n':' ');
}
}
}