题意描述

有一个\(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;
}

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

  1. [Codeforces 464E] The Classic Problem(可持久化线段树)

    [Codeforces 464E] The Classic Problem(可持久化线段树) 题面 给出一个带权无向图,每条边的边权是\(2^{x_i}(x_i<10^5)\),求s到t的最短路 ...

  2. Codeforces 464E. The Classic Problem

    题目大意 给定一张$n$个点, $m$条边的无向图,求$S$ 到$T$的最短路,其中边权都是$2^k$的形式$n,m,k<=10^5$,结果对$10^9+7$取模 题解 大佬好厉害 跑一边dij ...

  3. Codeforces 464E The Classic Problem (最短路 + 主席树 + hash)

    题意及思路 这个题加深了我对主席树的理解,是个好题.每次更新某个点的距离时,是以之前对这个点的插入操作形成的线段树为基础,在O(logn)的时间中造出了一颗新的线段树,相比直接创建n颗线段树更省时间. ...

  4. Codeforces 464E The Classic Problem(主席树+最短路+哈希,神仙题)

    题目链接 题意:给出一张 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边连接 \(u_i,v_i\),边权为 \(2^{w_i}\),求 \(s\) 到 \(t\) 的最短路. \( ...

  5. 【主席树维护mex】 【SG函数递推】 Problem H. Cups and Beans 2017.8.11

    Problem H. Cups and Beans 2017.8.11 原题: There are N cups numbered 0 through N − 1. For each i(1 ≤ i ...

  6. Codeforces 960 二进制构造子序列 完全二叉树shift模拟 主席树/MAP DP

    A #include <bits/stdc++.h> #define PI acos(-1.0) #define mem(a,b) memset((a),b,sizeof(a)) #def ...

  7. HDU 4729 An Easy Problem for Elfness(主席树)(2013 ACM/ICPC Asia Regional Chengdu Online)

    Problem Description Pfctgeorge is totally a tall rich and handsome guy. He plans to build a huge wat ...

  8. Codeforces 750E - New Year and Old Subsequence(线段树维护矩阵乘法,板子题)

    Codeforces 题目传送门 & 洛谷题目传送门 u1s1 我做这道 *2600 的动力是 wjz 出了道这个套路的题,而我连起码的思路都没有,wtcl/kk 首先考虑怎样对某个固定的串计 ...

  9. CF 464E The Classic Problem

    补一补之前听课时候的题. 考虑使用dij算法求最短路,因为边权存不下,所以考虑用主席树维护二进制位,因为每一次都只会在一个位置进行修改,所以可以暴力进位,这样均摊复杂度是对的. <算法导论> ...

随机推荐

  1. 阿里云telnet 3306端口失败

    在阿里云的服务器上安装了MySQL, 然后远程访问总是不通. 查询了很久,排查思路如下: 检查mysql是否启动 检查本机3306端口是否处于监听状态 检查阿里云控制台是否开启了安全限制 检查mysq ...

  2. bash:express:command not found

    重新设置了node_global和node_cache目录后,总是显示command not found 找了几篇博文后,终于解决了这个问题   步骤如下: 1.新建node_global和node_ ...

  3. JVM规范系列第6章:Java虚拟机指令集

    一条 Java 虚拟机指令由一个特定操作的操作码和零至多个操作所使用到的操作数所构成. 虚拟机指令 = 操作码 + 操作数. 其中,操作码值分别为 254(0xfe)和 255(0xff),助记符分别 ...

  4. C# 读取Json配置文件

    今天需要用到读取Json配置文件的helper   结果竟然没找到合适的    微软自己有一个 不过不支持.Net fw 4.0 于是自己在NewTonSoft.Json的基础上  加了点小小的封装  ...

  5. ABP+AdminLTE+Bootstrap Table权限管理系统第七节--登录逻辑及几种abp封装的Javascript函数库

    返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期         简介 经过前几节,我们已经解决数据库,模型,DTO,控制器和注入等问题.那么再来看一下登录逻辑.这 ...

  6. 记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)

    0x01 前言 最近在做代码审计的工作中遇到了一个难题,题目描述如下: <?php include 'flag.php'; if(isset($_GET['code'])){ $code = $ ...

  7. zookeeper 动态管理nginx配置

    假设我们有一个场景,所有服务器共享同一份配置文件,我们肯定不可能单独手动维护每台服务器,这时可以利用zookeeper的配置管理功能. 环境:python + nginx + zookeeper 目的 ...

  8. 分布式监控系统Zabbix--完整安装记录-批量添加主机和自动发现端口

    一.Zabbix-3.0.3批量添加主机的配置如下: 0)被监控机上要安装zabbix_agent,并配置好zabbix_agentd.conf (如下172.29.8.50是zabbix_serve ...

  9. LInux下设置账号有效时间 以及 修改用户名(同时修改用户组名和家目录)

    在linux系统中,默认创建的用户的有效期限都是永久的,但有时候,我们需要对某些用户的有效期限做个限定!比如:公司给客户开的ftp账号,用于客户下载新闻稿件的.这个账号是有时间限制的,因为是付费的.合 ...

  10. VS2015安装及单元测试

    今天跟大家分享一下我的VS2015的安装过程以及对单元测试的操作步骤.VS2015是一款非常好用的编程软件,内容很多很广泛,是深受欢迎的一款软件,较之于VC++6.0有着一些好处,对VC6.0++来说 ...