CodeForces - 464E - The Classic Problem

题面

传送门

题解

写的头疼

先看题, 明显是最短路, 关键是如何保存距离, 毕竟太大了

而且题目给的就是二进制, 就想到了要拆位,

一般的拆位用个可持续化 01trie 就好了, 这道题真的毒瘤还是 2进制下1e5 的范围

那只能可持续化权值线段树了, 好能存路径了, 但问题来了, 毕竟是二进制要考虑进位的

进位


先说为什么不能暴力进位, 看着组数据

100000 99999
1 2 0
2 3 1
...
49999 50000 49998
50000 50001 0
50000 50002 0
...
50000 100000 0
1 100000

在点50000之后就是 二进制下整个暴力进位, 每次相当于重新建一棵权值线段树,

时间复杂度就成了 n * n * logn(n = 5e4)

50001~100000之间建的线段树空间复杂度为 n * n * logn(n = 5e4)

时空全爆, 只不过是老题, 没人提供这组hack数据罢了


什么是二进制进位?

就是有一段连续的 11111, 你又在其中 + 1, 就进位了

说白了, 就是清空一段, 在让一个位置 + 1

而时空复杂度都是 2 * logn = logn

想象一颗满的权值线段树

左孩子要进位, 就清空, 复杂度 logn,

然后让 右孩子 + 1, 如果右孩子的左孩子要进位, 又是一条log, 但是是 log(n/2)的

不断深入, 最后复杂度应为 logn + log(n/2) + log(n/4) ... < 2logn

对于右孩子还进位的返回不就完事了, 那是该当前这颗树的祖先的右孩子改进位了

比较大小

这里不能直接去比, 看着想 logn 其实不然,

    int cmp(int x, int y, int l, int r) {
        if (tr[x].val == 0 && tr[y].val == 0) return 0;
        else if (tr[x].val == 0) return -1;
        else if (tr[y].val == 0) return 1;
        if (l == r) return tr[x].val < tr[y].val ? -1 : tr[x].val > tr[y].val;
        int mid = l + r >> 1, ans = cmp(tr[x].r, tr[y].r, mid + 1, r);
        return ans ? ans : cmp(tr[x].l, tr[y].l, l, mid);
    }

这就是不断的再找最远的右孩子知道出现某个二进制位置不一样, 复杂明显成线性了

那咋办, hash 啊, 我怕出错用了两次has, 不过选的好一次就行

但是随着acm的发展, 正规比赛时卡单has的, 一般双/三哈希卡不住, 这道题数据水了倒是单has能过

代码

struct CHT {
    static const int N = 1e5 + 20, mod = 1e9 + 7, mod1 = 13331;

    struct Node { int l, r, val, has[2]; } tr[N * 40];

    int rt[N], tot, s[2][N] = { {1}, {1} };

    void init() {
        rep(i, 1, 1e5 + 16)
            s[0][i] = ((ll)s[0][i - 1] << 1) % mod,
            s[1][i] = ((ll)s[1][i - 1] << 1) % mod1;
    }

    void clear(int& x, int y, int l, int r, int L, int R) {
        if (L <= l && r <= R) { x = 0; return; }
        tr[x = ++tot] = tr[y];
        int mid = l + r >> 1;
        if (mid >= L) clear(tr[x].l, tr[y].l, l, mid, L, R);
        if (mid < R) clear(tr[x].r, tr[y].r, mid + 1, r, L, R);
        tr[x].val = tr[tr[x].l].val + tr[tr[x].r].val;
        tr[x].has[0] = ((ll)tr[tr[x].l].has[0] + tr[tr[x].r].has[0]) % mod;
        tr[x].has[1] = ((ll)tr[tr[x].l].has[1] + tr[tr[x].r].has[1]) % mod1;
    }

    bool update(int& x, int y, int l, int r, int val) {
        if (tr[y].val == r - l + 1) {
            clear(x, y, l, r, val, r);
            return 1;
        }
        tr[x = ++tot] = tr[y];
        if (l == r) return tr[x].val = 1, tr[x].has[0] = s[0][l], tr[x].has[1] = s[1][l], 0;
        int mid = l + r >> 1, f = 0;
        if (mid >= val) {
            if (update(tr[x].l, tr[y].l, l, mid, val))
                f = update(tr[x].r, tr[y].r, mid + 1, r, mid + 1);
        }
        else f = update(tr[x].r, tr[y].r, mid + 1, r, val);
        tr[x].has[0] = ((ll)tr[tr[x].l].has[0] + tr[tr[x].r].has[0]) % mod;
        tr[x].has[1] = ((ll)tr[tr[x].l].has[1] + tr[tr[x].r].has[1]) % mod1;
        return tr[x].val = tr[tr[x].l].val + tr[tr[x].r].val, f;
    }

    int cmp(int x, int y) {
        if (tr[x].val == 0 && tr[y].val == 0) return 0;
        else if (tr[x].val == 0) return -1;
        else if (tr[y].val == 0) return 1;
        if (tr[tr[x].r].has[0] != tr[tr[y].r].has[0] || tr[tr[x].r].has[1] != tr[tr[y].r].has[1])
            return cmp(tr[x].r, tr[y].r);
        return cmp(tr[x].l, tr[y].l);
    }
} T;

const int N = 1e5 + 16;

int n, m, _, k;

struct Node { int x, rt; bool operator <(Node a) const { return T.cmp(rt, a.rt) == 1; } };

int main() {
    IOS; cin >> n >> m; vector<vector<PII>> h(n + 1);
    rep(i, 1, m) {
        int u, v, c; cin >> u >> v >> c;
        h[u].pb({ v, c }); h[v].pb({ u, c });
    }
    int s, t; cin >> s >> t; T.init();
    priority_queue<Node> q; q.push({ s, 0 });
    vector<bool> v(n + 1); VI pre(n + 1);
    while (!q.empty()) {
        int x = q.top().x, tmp; q.pop();
        if (v[x]) continue; v[x] = 1;
        if (x == t) break;
        for (auto& e : h[x]) {
            if (v[e.fi]) continue;
            T.update(tmp, T.rt[x], 0, N, e.se);
            if (!T.rt[e.fi] || T.cmp(T.rt[e.fi], tmp) == 1)
                q.push({ e.fi, T.rt[e.fi] = tmp }), pre[e.fi] = x;
        }
    }
    if (!v[t]) { cout << -1; return 0; } T.init();
    VI ans; for (int i = t; i; ans.pb(i), i = pre[i]);
    cout << T.tr[T.rt[t]].has[0] << '\n' << ans.size() << '\n';
    per(i, ans.size() - 1, 0) cout << ans[i] << ' ';
    return 0;
}
posted @ 2020-12-08 16:12  洛绫璃  阅读(154)  评论(0编辑  收藏  举报