CodeForces 464E The Classic Problem | 呆克斯歘 主席树维护高精度

题意描述

有一个\(n\)点\(m\)边的无向图,第\(i\)条边的边权是\(2^{a_i}\)。求点\(s\)到点\(t\)的最短路长度(对\(10^9 + 7\)取模)。

题解

思路很简单——用主席树维护每个点的\(dis\)。因为每次更新某个点\(v\)的\(dis_v\)的时候,新的\(dis_v\)都是某个点\(u\)的\(dis_u + 2^{w_{u, v}}\),相当于在原先\(u\)对应的主席树基础上修改,得到新的一棵主席树,作为\(v\)对应的主席树。

主席树(线段树)维护二进制高精度怎么维护呢?像松松松那么维护就好了 = =

需(wǒ)要(fàn)注(guò)意的问题:

  1. 如果你用priority_queue来做Dijkstra,又中途修改了节点对应的dis,会影响堆的性质,会WA。正确做法是在priority_queuepair<节点编号,当前dis>
  2. 主席树的空间要适当优化优化?例如查询操作的时候,pushdown会创造新的节点,但是以后就不会用到这群节点了,于是一次完整的查询操作之后把这些新节点都删掉就好了,空间可以得到明显优化。

代码

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#define space putchar(' ')
#define enter putchar('\n')
typedef long long ll;
using namespace std;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 100100, M = 40000007, mod = 1000000007, P = 1000000021;
int n, s, t, maxn = 100098, m, hsh100[N], hsh111[N], ans100[N], ans111[N];
int ecnt, adj[N], nxt[2*N], go[2*N], w[2*N], pre[N], stk[N], top;
int ls[M], rs[M], hsh[M], ans[M], tot, root[N];
bool lazy[M], vis[N];
void adde(int u, int v, int ww){
    go[++ecnt] = v;
    nxt[ecnt] = adj[u];
    adj[u] = ecnt;
    w[ecnt] = ww;
}
int newnode(int old){
    int k = ++tot;
    ls[k] = ls[old], rs[k] = rs[old];
    hsh[k] = hsh[old], ans[k] = ans[old], lazy[k] = lazy[old];
    return k;
}
int pushdown(int k){
    if(!lazy[k]) return newnode(k);
    k = newnode(k);
    lazy[k] = 0;
    ls[k] = newnode(ls[k]), rs[k] = newnode(rs[k]);
    lazy[ls[k]] = lazy[rs[k]] = 1;
    hsh[ls[k]] = hsh[rs[k]] = ans[ls[k]] = ans[rs[k]] = 0;
    return k;
}
int change0(int k, int l, int r, int ql, int qr){
    if(ql <= l && qr >= r) return k = newnode(k), lazy[k] = 1, hsh[k] = ans[k] = 0, k;
    k = pushdown(k);
    int mid = (l + r) >> 1;
    if(ql <= mid) ls[k] = change0(ls[k], l, mid, ql, qr);
    if(qr > mid) rs[k] = change0(rs[k], mid + 1, r, ql, qr);
    hsh[k] = (hsh[ls[k]] + (ll)hsh[rs[k]] * hsh100[mid - l + 1]) % P;
    ans[k] = (ans[ls[k]] + (ll)ans[rs[k]] * ans100[mid - l + 1]) % mod;
    return k;
}
int change1(int k, int l, int r, int p){
    if(l == r) return k = newnode(k), lazy[k] = 0, hsh[k] = ans[k] = 1, k;
    k = pushdown(k);
    int mid = (l + r) >> 1;
    if(p <= mid) ls[k] = change1(ls[k], l, mid, p);
    else rs[k] = change1(rs[k], mid + 1, r, p);
    hsh[k] = (hsh[ls[k]] + (ll)hsh[rs[k]] * hsh100[mid - l + 1]) % P;
    ans[k] = (ans[ls[k]] + (ll)ans[rs[k]] * ans100[mid - l + 1]) % mod;
    return k;
}
int find0(int k, int l, int r, int ql, int qr){
    if(hsh[k] == hsh111[r - l + 1] && ans[k] == ans111[r - l + 1]) return -1;
    if(l == r) return l;
    k = pushdown(k);
    int mid = (l + r) >> 1;
    if(ql > mid) return find0(rs[k], mid + 1, r, ql, qr);
    int ret = find0(ls[k], l, mid, ql, qr);
    if(ret != -1) return ret;
    return find0(rs[k], mid + 1, r, ql, qr);
}
int add(int k, int p){
    int mem_tot = tot;
    int q = find0(k, 0, maxn, p, maxn);
    tot = mem_tot;
    k = change1(k, 0, maxn, q);
    if(p < q) k = change0(k, 0, maxn, p, q - 1);
    return k;
}
bool diff(int k1, int k2, int l, int r){
    if(l == r) return hsh[k1] < hsh[k2];
    k1 = pushdown(k1), k2 = pushdown(k2);
    int mid = (l + r) >> 1;
    if(hsh[rs[k1]] == hsh[rs[k2]] && ans[rs[k1]] == ans[rs[k2]])
        return diff(ls[k1], ls[k2], l, mid);
    else return diff(rs[k1], rs[k2], mid + 1, r);
}
struct Data {
    int node, root;
    bool operator < (const Data &b) const {
        int mem_tot = tot;
        bool ret = diff(b.root, root, 0, maxn);
        tot = mem_tot;
        return ret;
    }
};
priority_queue <Data> que;

int main(){

    read(n), read(m);
    hsh100[0] = ans100[0] = 1;
    for(int i = 1; i <= maxn + 1; i++){
        hsh100[i] = hsh100[i - 1] * 2 % P;
        ans100[i] = ans100[i - 1] * 2 % mod;
        hsh111[i] = (hsh100[i] - 1 + P) % P;
        ans111[i] = (ans100[i] - 1 + mod) % mod;
    }
    for(int i = 1, u, v, ww; i <= m; i++)
        read(u), read(v), read(ww), adde(u, v, ww), adde(v, u, ww);
    read(s), read(t);
    root[0] = add(0, maxn - 1);
    for(int i = 1; i <= n; i++)
        if(i != s) root[i] = root[0];
    que.push((Data){s, root[s]});
    while(!que.empty()){
        int u = que.top().node;
        que.pop();
        if(vis[u]) continue;
        vis[u] = 1;
        for(int e = adj[u], v; e; e = nxt[e]){
            v = go[e];
            int tmp = add(root[u], w[e]);
            if(diff(tmp, root[v], 0, maxn))
                root[v] = tmp, pre[v] = u, que.push((Data){v, root[v]});
        }
    }
    if(ans[root[t]] == ans100[maxn - 1] && hsh[root[t]] == hsh100[maxn - 1])
        return puts("-1"), 0;
    write(ans[root[t]]), enter;
    stk[++top] = t;
    while(pre[stk[top]]) stk[top + 1] = pre[stk[top]], top++;
    write(top), enter;
    while(top) write(stk[top--]), top ? space : enter;

    return 0;
}

原文地址:https://www.cnblogs.com/RabbitHu/p/CF464E.html

时间: 2024-10-13 08:08:42

CodeForces 464E The Classic Problem | 呆克斯歘 主席树维护高精度的相关文章

BZOJ 3218 A+B Problem(最大流 + 主席树优化建图)

题目:A+B Problem 感谢 Nietzsche 在省选紧迫之际花 39' 给我讲这道题. 这题我并没有想出来,感觉又浪费一道好题了. 需要用最小割,建模方式如下(假设若 2 取黑色,1 取白色会使 2 为奇怪方格): 跑一边最大流,求出最小割,用所有的 W + 所有的 B - 最小割,就是答案. 不过,对于每一个结点 2,在寻找像 1 这样(li <= aj <= ri)的结点时,总不能一个一个枚举吧? O(n2) T 飞. 所以,需要用主席树优化一下.线段树优化建图笔记. 代码未完待

CF 464E The Classic Problem

补一补之前听课时候的题. 考虑使用dij算法求最短路,因为边权存不下,所以考虑用主席树维护二进制位,因为每一次都只会在一个位置进行修改,所以可以暴力进位,这样均摊复杂度是对的. <算法导论>给了证明:对于一个有$k$位的二进制计数器,假设每一次都从第0位$+1$,那么我们发现执行$n$次加法之后,发现第零位会变$\left \lfloor \frac{n}{1} \right \rfloor$次,第一位会变$\left \lfloor \frac{n}{2} \right \rfloor$次.

Codeforces Round #426 (Div. 2) D. The Bakery(线段树维护dp)

题目链接: Codeforces Round #426 (Div. 2) D. The Bakery 题意: 给你n个数,划分为k段,每段的价值为这一段不同的数的个数,问如何划分,使得价值最大. 题解: 考虑dp[i][j]表示划分为前j个数划分为i段的最大价值,那么这就是一个n*n*k的dp, 考虑转移方程dp[i][j]=max{dp[i][k]+val[k+1][j]},我们用线段树去维护这个max,线段树上每个节点维护的值是dp[i][k]+val[k+1][j],对于每加进来的一个数a

Codeforces 464E #265 (Div. 1) E. The Classic Problem 主席树+Hash

E. The Classic Problem http://codeforces.com/problemset/problem/464/E 题意:给你一张无向带权图,求S-T的最短路,并输出路径.边权为2^xi.xi≤105,n≤105,m≤105. 想法:边权太大了,可以用数组按二进制存下来.带高精度跑太费事了. 观察一下,这里距离的更新:c=(a,b),用dis[a]更新dis[b] ①dis[b][c]=0,直接赋为1.只有一个数字改变. ②dis[b][c]=1,需要进位.考虑极端情况数

http://codeforces.com/contest/575/problem/B

题目链接: http://codeforces.com/contest/575/problem/B 题解: 代码: #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = 1e5 + 10; const int DEG = 22; const in

Codeforces 442B Andrey and Problem(贪心)

题目链接:Codeforces 442B Andrey and Problem 题目大意:Andrey有一个问题,想要朋友们为自己出一道题,现在他有n个朋友,每个朋友想出题目的概率为pi,但是他可以同时向多个人寻求帮助,不过他只能要一道题,也就是如果他向两个人寻求帮助,如果两个人都成功出题,也是不可以的. 解题思路:贪心,从概率最大的人开始考虑,如果询问他使得概率变大,则要询问. #include <cstdio> #include <cstring> #include <a

http://codeforces.com/contest/741/problem/B B. Arpa&#39;s weak amphitheater and Mehrdad&#39;s valuable Hoses

题意: 给出上限体重W 然后还给出每个人的体重wi 和 魅力值 bi 互为伙伴的对(xi, yi) 可以凑成group 思路: 并查集找出所有的group 暴力背包 对于每一个group 要选出这一组内选一个人时的最优结果, 如果所有人的体重和小于等于W,还得考虑选所有人的情况 #include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include &l

CodeForces 776D The Door Problem【并查集】

CodeForces 776D The Door Problem[并查集]并查集 设 f 1--m 表示 开的情况 m+1--2*m 表示关的情况 对于每盏灯 如果他 是关的 则 x--y x+m--y+m 表示要同关 或者同开 如果他 是开的 则 x+m--y x--y+m 表示一个关 一个开如果一盏灯 的 x 连向 了 x+m 则表示是矛盾了 那么久是错误的 题意:给你n个门,和m组开关,每扇门都有两个开关控制,每个开关控制x扇门,如果选择了某组开关,则使这组开关里的每个开关控制的所有的门按

Codeforces 30D King&#39;s Problem? 模拟

首先将n个点排序,找出排序后的K,然后分情况讨论. 当 k == n+1时,显然是 k->1->n || k->n->1这两种的较小值,因为三角形的两边之和大于第三边. 当1 <= k && k <= n 时: 1 , k -> 1 -> n+1 -> k+1 ->n  ||  k -> n -> n+1 -> k-1 -> 1,当k+1 || k-1 不存在时将对应步骤忽略. 2 , k - > 1