闲话 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} \}\),第三个值表明该值是通过哪种方式更新的。我们在下面会看到这两种策略有着不同的影响。

考虑如何更新。一个节点更新其他点的策略也只有两种:

  1. 通过特殊边以正常的 dij 策略更新。若 \(u\rightarrow v\) 有一条长度为 \(d\) 的边,塞进去一个 \(\{v, dis[u] + d, \text{false}\}\)。这里 \(id\)\(dis\) 的值是正常的 dij 节点。
  2. 跳跃到与它通过非特殊边相连的节点。塞进去一个 \(\{u, dis[u] + a[u], \text{true}\}\)。这里的 \(id\) 标明通过非特殊边跳跃的起点,\(dis\) 表示这次跳跃的终点被更新成的值。

然后在优先队列里拿出一个节点时考虑它的更新策略:

  1. \(\{id, dis, \text{false}\}\) 这时考虑正常 dij,按如上两种策略更新所有与 \(id\) 通过特殊边相连的点。
  2. \(\{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;
}
posted @ 2022-09-26 19:48  joke3579  阅读(121)  评论(2编辑  收藏  举报