闲话 22.9.26
闲话
谢谢你,jjdw
似乎是 ke-ta 老师的灵梦
如果有biku出没的话请补一下出处谢谢你
为jjdw 的图 引流 Link
今天只有一道题(
最近没怎么写题 校内的题就得该好久
希望后天不simulation的时候不会被拉去验题
怎么感觉闲话越来越水了
是不是总感觉没有时间才导致的
好久没听饭的歌了
感觉有戒断反应了(
以及最近在哼 Non-Breath Oblige 的时间多起来了
是不是人越没有什么就越想得到什么呢
わたし先生の前でだけ真面目ぶる子嫌なんだ
そう言うとあなたは困り顔で俯いていた
実際はどうでもよかった 本題は別のものだった
最低が隠されている
良い子はみんな大人になった
夢から醒めた
杂题
我等几天再去切risrqnis
给一张 \(n\) 个点的有向完全图,其中 \(u\rightarrow v\) 的边权为 \(a_u\). 有 \(m\) 次修改,每次修改给定 \(u, v, d\),表示将 \(u\rightarrow v\) 的边权改为 \(d\). 求所有点对 \(i,j(i\neq j)\) 间最短路之和。
\(n \le 10^5, m\le 3\times 10^3\)
我们将修改对应的边称作特殊边,特殊边的两端点称作特殊点。容易发现任意非特殊点 \(u\) 对答案的贡献是 \((n-1)\times a_u\)。对于特殊点间最短路,显然只有 \(a\) 值最小的非特殊点才有贡献。我们称这个非特殊点为最小点,并将该非特殊点纳入特殊点集合,随后计算所有非特殊点的贡献并删除这些点。留下的 \(O(m)\) 个点成为一张新图。
对于 \(m\) 较小的情况可以直接在新图内跑全源最短路。而较大时可以考虑使用线段树区间取 \(\min\) 实现 \(O(m^2 \log m)\) 的复杂度。
现在考虑另一种做法。
我们考虑一个点会如何被更新。显然 1. 沿着特殊边更新 2. 从最小点跳过来。
我们直接使用正常 dij 的策略,使用优先队列维护 \(\{ id, dis, \text{boolean} \}\),第三个值表明该值是通过哪种方式更新的。我们在下面会看到这两种策略有着不同的影响。
考虑如何更新。一个节点更新其他点的策略也只有两种:
- 通过特殊边以正常的 dij 策略更新。若 \(u\rightarrow v\) 有一条长度为 \(d\) 的边,塞进去一个 \(\{v, dis[u] + d, \text{false}\}\)。这里 \(id\) 和 \(dis\) 的值是正常的 dij 节点。
- 跳跃到与它通过非特殊边相连的节点。塞进去一个 \(\{u, dis[u] + a[u], \text{true}\}\)。这里的 \(id\) 标明通过非特殊边跳跃的起点,\(dis\) 表示这次跳跃的终点被更新成的值。
然后在优先队列里拿出一个节点时考虑它的更新策略:
- \(\{id, dis, \text{false}\}\) 这时考虑正常 dij,按如上两种策略更新所有与 \(id\) 通过特殊边相连的点。
- \(\{id, dis, \text{true}\}\) 跳跃通过 \(a_u\) 完成,这表明如果 \(u\rightarrow v\) 有特殊边则无法通过此方法更新,因为跳跃通过的那条边已经没了。因此标记所有与 \(id\) 通过特殊边相邻的点,然后使用 \(dis\) 更新所有未标记的节点(终点)。
跑出来 \(dis\) 数组后更新答案。对于特殊点直接加 \(dis\) ,剩下的点取出 \(dis[u] + a_u\) 的最大值然后乘入 \(n - 特殊点数量\) 就行。
注意每个点只会被更新一次。但是我们在更新时需要扫每个点是否需要通过 2 更新,如果直接打 vis 标记的话时间会直接飙到 \(O(m^2 \log m)\) 单次。因此考虑一个并茶几。
我们只有在 \(fa[u] = u\) 时更新 \(u\),更新完后执行 \(fa[u] = u+1\)。每次只需要从 \(i = \text{find}(1)\) 开始,每次执行 \(i = \text{find}(i+1)\) 就能获得均摊 \(O(n \ \alpha (n))\) 的复杂度。
code :
int fa[N];
vector < pair<int,int> > g[N];
int find(int u) { return u == fa[u] ? u : fa[u] = find(fa[u]); }
int add(int u) { if (!id[u]) stk[++cnt] = u, id[u] = cnt; }
struct pid {
int pos; bool lty; ll val;
pid(int _p, ll _v, bool _l) : pos(_p), val(_v), lty(_l) {}
bool operator < (const pid & b) const {
return val > b.val;
}
} ; priority_queue < pid > q;
ll dis[N];
void insert(int x, int val) {
if (fa[x] == x) {
fa[x] = x + 1;
dis[x] = val;
q.emplace(x, dis[x] + a[stk[x]], true);
for (auto [v, d] : g[x]) {
if (dis[v] > dis[x] + d){
dis[v] = dis[x] + d;
q.emplace(v, dis[v], false);
}
}
}
}
void ufdij(int st) {
rep(i,1,cnt+1) dis[i] = 1e18, fa[i] = i;
dis[st] = 0; q.emplace(st, 0, false);
while (!q.empty()) {
auto x = q.top(); q.pop();
if (!x.lty) {
insert(x.pos, x.val);
} else {
for (auto [v, d] : g[x.pos]) vis[v] = true;
for (int i = find(1); i <= cnt; i = find(i+1)) {
if (!vis[i]) insert(i, x.val);
}
for (auto [v, d] : g[x.pos]) vis[v] = false;
}
}
}
signed main() {
get(n, m);
rep(i,1,n) get(a[i]);
rep(i,1,m) {
get(t1, t2, t3); add(t1); add(t2);
g[id[t1]].emplace_back(id[t2], t3);
} valk = 1e9;
rep(i,1,n) if (!id[i] and a[i] < valk) rem = i, valk = a[i];
if (valk != 1e9) add(rem);
rep(i,1,n) if (!id[i]) ans += (n - 1) * a[i];
rep(i,1,cnt) {
ll mn = 1e18; ufdij(i);
rep(j,1,cnt) ans += dis[j], mn = min(mn, dis[j] + a[stk[j]]);
ans += (n - cnt) * mn;
} cout << ans << endl;
}
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat220926.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。