CF464E The Classic Problem 题解

Description

洛谷传送门

Solution

emm……这是我为数不多的黑题之一,所以来写篇博客记录一下。

我们发现边权过大,只能用高精度来算,但是这样的复杂度太劣了,无法通过此题。

观察到边权只能是 \(2^x\),所以我们可以给它压成二进制数,然后跑最短路时单点加。

我们再来考虑一下 \(dijkstra\) 需要哪些操作:

  • 区间加

  • 比大小

先来看区间加,如果第 \(x\) 位为 0,那么赋值为 1 即可,如果第 \(x\) 位为一,那么我们需要向上找第一个为 0 的位,然后这一段区间赋值为 0,第一个为 0 的位赋值为 1。

这个查找操作可以使用线段树上二分来实现,据说暴力查找也是可以过的,数据比较水。

那区间赋值为 0 如何实现呢?主席树似乎不太能支持修改。我们可以建一棵全 0 主席树,然后修改时直接把这棵全 0 的树拍上去。

再来看区间比大小,直接比较区间和即可。

然后就是代码实现了,个人认为非常有助于提升码力,也让我对主席树的理解更深了一步。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#define ull unsigned long long
#define ll long long
#define ls(x) t[x].l
#define rs(x) t[x].r

using namespace std;

inline int read(){
    int x = 0;
    char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}

const ll mod = 1e9 + 7;
const int N = 2e5 + 10;
int n, m, S, T;
struct node{
    int v, w, nxt;
}edge[N << 1];
int head[N], tot;
int inf;
ll fac[N];
struct Tree{
    ll sum;//sum:区间和
    int l, r, num;//num:区间 1 的个数
    friend bool operator == (Tree a,  Tree b){
        return a.sum == b.sum && a.num == b.num;
    }
}t[N * 40];
int root[N], cnt;//跟,节点编号
int ans[N], path, pre[N];//记录路径
bool vis[N];

inline void add(int x, int y, int z){
    edge[++tot] = (node){y, z, head[x]};
    head[x] = tot;
}

inline void pushup(int rt){
    t[rt].num = t[ls(rt)].num + t[rs(rt)].num;
    t[rt].sum = (t[ls(rt)].sum + t[rs(rt)].sum) % mod;
}

inline void build(int &rt, int l, int r, int val){//建树
    if(!rt) rt = ++cnt;//一定要判断,不然会 RE
    if(l == r){
        t[rt].sum = fac[l] * val, t[rt].num = val;
        return;
    }
    int mid = (l + r) >> 1;
    build(ls(rt), l, mid, val);
    build(rs(rt), mid + 1, r, val);
    pushup(rt);
}

inline int query(int rt, int L, int R, int l, int r){//查询区间 1 的个数
    if(L <= l && r <= R) return t[rt].num;
    int mid = (l + r) >> 1;
    int res = 0;
    if(L <= mid) res += query(ls(rt), L, R, l, mid);
    if(R > mid) res += query(rs(rt), L, R, mid + 1, r);
    return res;
}

inline void update(int &rt, int l, int r, int val){//加点, 0 改为 1
    t[++cnt] = t[rt];
    rt = cnt;
    if(l == r){
        t[rt].sum = fac[l], t[rt].num = 1;
        return;
    }
    int mid = (l + r) >> 1;
    if(val <= mid) update(ls(rt), l, mid, val);
    else update(rs(rt), mid + 1, r, val);
    pushup(rt);
}

inline void clear(int &x, int y, int L, int R, int l, int r){//区间赋 0,把 y(全 0 树) 全部贴到 x 上
    if(L <= l && r <= R){
        x = y;
        return;
    }
    int mid = (l + r) >> 1, z = ++cnt;
    t[z] = t[x];
    if(L <= mid) clear(ls(z), ls(y), L, R, l, mid);
    if(R > mid) clear(rs(z), rs(y), L, R, mid + 1, r);
    pushup(x = z);
}

inline int search(int rt, int l, int r, int val){//线段树上二分,向上找第一个不为 1 的位置
    if(l == r) return l;
    int mid = (l + r) >> 1;
    if(val > mid) return search(rs(rt), mid + 1, r, val);//查找权值在右子树
    if(query(ls(rt), val, mid, l, mid) == mid - val + 1) return search(rs(rt), mid + 1, r, mid + 1);//左子树全为 1 且 val <= mid,到右子树查询
    return search(ls(rt), l, mid, val);//否则在左子树查询
}

inline bool cmp(int x, int y, int l, int r){
    if(l == r) return t[x].sum < t[y].sum;
    int mid = (l + r) >> 1;
    if(t[rs(x)] == t[rs(y)]) return cmp(ls(x), ls(y), l, mid);
    return cmp(rs(x), rs(y), mid + 1, r);
}

struct Que{
    int id, rt;
    friend bool operator < (Que a, Que b){
        return cmp(b.rt, a.rt, 0, N - 1);
    }
};

inline void dijkstra(){
    priority_queue <Que> q;
    for(int i = 1; i <= n; ++i)
        root[i] = inf;
    root[S] = root[0];
    q.push((Que){S, root[S]});
    while(!q.empty()){
        int x = q.top().id;
        q.pop();
        if(vis[x]) continue;
        vis[x] = 1;
        for(int i = head[x]; i; i = edge[i].nxt){
            int y = edge[i].v, rt_x = root[x], res = cnt;
            int pos = search(root[x], 0, N - 1, edge[i].w);
            update(rt_x, 0, N - 1, pos);
            if(edge[i].w < pos) clear(rt_x, root[0], edge[i].w, pos - 1, 0, N - 1);
            if(cmp(rt_x, root[y], 0, N - 1)) pre[y] = x, q.push((Que){y, root[y] = rt_x});
            else cnt = res;
        }
    }
}

int main(){
    n = read(), m = read();
    for(int i = 1; i <= m; ++i){
        int u = read(), v = read(), w = read();
        add(u, v, w), add(v, u, w);
    }
    S = read(), T = read();
    fac[0] = 1;
    for(int i = 1; i < N; ++i)
        fac[i] = fac[i - 1] * 2 % mod;
    build(root[0], 0, N - 1, 0), build(inf, 0, N - 1, 1);
    dijkstra();
    if(root[T] == inf){
        puts("-1");
        return 0;
    }
    for(int x = T; x != S; x = pre[x]) ans[++path] = x;
    ans[++path] = S;
    printf("%lld\n%d\n", t[root[T]].sum, path);
    for(int i = path; i >= 1; --i)
        printf("%d ", ans[i]);
    return 0;
}

\[\_EOF\_ \]

posted @ 2021-10-30 08:24  xixike  阅读(53)  评论(0编辑  收藏  举报