CF464E The Classic Problem(主席树+哈希+最短路)

CF464E The Classic Problem

  • problem
  • solution
  • code

problem

题目链接

solution

经典题。

本题很特别的是每条边的边权都是 222 的幂,而且数据读入的是指数。

所以可以将路径权值看作一个二进制串,每加一条边就是对应二进制位 +1+1+1,当然会有相应的进位操作。

用一棵线段树维护一个二进制串。

考虑加边的操作。

  • 如果该位原先是 000 ,那么直接 +1+1+1 ,即线段树的单点修改。

  • 如果该位原先是 111,那么就会导致进位问题,需要从当前位置开始找一段连续最长都为 111 的串,在第一个 000 的位置停下。然后将最终位置 +1+1+1,中间连续一段全都清零。即线段树的区间修改和单点修改。

    从当前位置开始找一段连续的串,可以二分长度然后判断,但是这样线段树一次操作就是 O(log⁡2n)O(\log^2n)O(log2n) 的,还有最短路的部分呢?!

    所以我们在线段树上二分实现,降为 O(log⁡n)O(\log n)O(logn)

最短路就是堆优化的 djikstra\text{djikstra}djikstra

只不过比较两个字符串的大小,需要重载排序规则,所以实现是手打堆。

比较字符串大小肯定是哈希,所以线段树还要顺便维护一下串的哈希值,从高位开始比较,时间是 O(log⁡n)O(\log n)O(logn) 的。

最后是最短路的收缩,一个点可能更新多个点的最短路,但是多个点之间是独立的。

所以最短路径串不能改在原来的上面,每次修改都是产生一个新串。

因此我们采用主席树,动态开点。

总时间复杂度 :O(nlog⁡2n):O(n\log^2n):O(nlog2n)

部分具体实现含义可见代码注释。

code

#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long //自然溢出
#define mod 1000000007
#define maxn 400005
#define MAX 100040 //2^1e5叠加还要进位 开1e5+5不够
struct node { int to, nxt, w; }E[maxn];
int S, T, n, m, cnt = 1;
ull base2[maxn];
int head[maxn], lst[maxn], root[maxn], base1[maxn];
bool vis[maxn];
stack < int > ans;void addedge( int u, int v, int w ) {E[cnt] = { v, head[u], w };head[u] = cnt ++;
}namespace sgt { 
/*
[0,X]就表示[0,X]的二进制位 进位是往右边进的 所以rson二进制级别高于lson
因为堆优化的dijkstra u点可能会导致若干个v点的最短路 而每一个v之间是独立的
所以得用主席树
一棵线段树其实本质是一个二进制串 主席树相当于是在改串
*/#define mid ( ( l + r ) >> 1 )#define maxm maxn * 20int cnt;int hash1[maxm], sum[maxm], lson[maxm], rson[maxm]; //sum[now]:区间中二进制位为1的个数ull hash2[maxm];void pushup( int now ) {sum[now] = sum[lson[now]] + sum[rson[now]];hash1[now] = ( hash1[lson[now]] + hash1[rson[now]] ) % mod;hash2[now] = hash2[lson[now]] + hash2[rson[now]];}int build( int x, int l = 0, int r = MAX ) {//初始化串上的每个位置都是数字xint now = ++ cnt;if( l == r ) { sum[now] = x;hash1[now] = base1[l] * x;hash2[now] = base2[l] * x;return now;}lson[now] = build( x, l, mid );rson[now] = build( x, mid + 1, r );pushup( now );return now;}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return sum[now];return query( lson[now], l, mid, L, R ) + query( rson[now], mid + 1, r, L, R );}int query( int now, int pos, int l = 0, int r = MAX ) {//在pos位+1 查询从pos开始的最长连续的1 即从pos开始的第一个0位置 对应的是区间查询if( l == r ) return l;if( pos > mid ) return query( rson[now], pos, mid + 1, r ); if( query( lson[now], l, mid, pos, mid ) == mid - pos + 1 ) return query( rson[now], mid + 1, mid + 1, r );//打成了 query(rson[now],pos,mid+1,r) 调半天哭了elsereturn query( lson[now], pos, l, mid );}int modify( int lst, int pos, int l = 0, int r = MAX ) {//进位 前面的若干连续1导致了向pos进位(pos位一定为0)的结果 对应的是单点修改int now = ++ cnt;lson[now] = lson[lst], rson[now] = rson[lst];if( l == r ) { sum[now] = 1;hash1[now] = base1[l];hash2[now] = base2[l];return now;}if( pos <= mid ) lson[now] = modify( lson[lst], pos, l, mid );else rson[now] = modify( rson[lst], pos, mid + 1, r );pushup( now );return now;}int modify( int x, int y, int L, int R, int l, int r ) {//巧妙运用root[0](最开始的线段树)全是0 所以在修改区间内就直接指向root[0] 指针即可//没必要做无谓的动态开点 浪费空间if( R < l or r < L ) return x;if( L <= l and r <= R ) return y;int now = ++ cnt;lson[now] = modify( lson[x], lson[y], L, R, l, mid );rson[now] = modify( rson[x], rson[y], L, R, mid + 1, r );pushup( now );return now;}int add( int rt, int w ) { //这条边权值为2^w 对应操作为在第w位加1int pos = query( rt, w ); //找到可能触发进位后的第一个为0位置int now = modify( rt, pos );if( pos == w ) return now; //没有触发进位 就在原本的位置+1 就不会有后面的清除else return modify( now, root[0], w, pos - 1, 0, MAX ); //一旦进位到pos意味着[w,pos)全都是1 需要将这一段清0}bool same( int x, int y ) { return sum[x] == sum[y] and hash1[x] == hash1[y] and hash2[x] == hash2[y];}bool compare( int x, int y, int l = 0, int r = MAX ) { //比较x这棵树代表的二进制串和y代表的二进制串的大小 优先从高位(右儿子)开始i比较//1代表x<=y 0代表x>yif( l == r ) return sum[x] <= sum[y];if( same( rson[x], rson[y] ) )return compare( lson[x], lson[y], l, mid );elsereturn compare( rson[x], rson[y], mid + 1, r );}}struct heap { int siz, cnt, root;int id[maxn], rt[maxn], dis[maxn], lson[maxn], rson[maxn];int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( sgt :: compare( rt[y], rt[x] ) ) swap( x, y );rson[x] = merge( rson[x], y );if( dis[rson[x]] > dis[lson[x]] ) swap( lson[x], rson[x] ); //启发式合并dis[x] = dis[rson[x]] + 1;return x;}   void push( int x, int t ) { siz ++, cnt ++;id[cnt] = x;rt[cnt] = t;root = merge( root, cnt ); }void pop() { siz --; root = merge( lson[root], rson[root] ); }int top() { return id[root]; }bool empty() { return siz == 0; }}q;void dijkstra() {int ori = sgt :: build( 1 );for( int i = 1;i <= n;i ++ ) root[i] = ori;root[0] = root[S] = sgt :: build( 0 );q.push( S, root[S] );while( ! q.empty() ) {int u = q.top(); q.pop();if( vis[u] ) continue;vis[u] = 1;for( int i = head[u];i;i = E[i].nxt ) {int v = E[i].to, w = E[i].w;if( vis[v] ) continue;int New = sgt :: add( root[u], w );if( sgt :: compare( root[v], New ) ) continue;root[v] = New, lst[v] = u;q.push( v, root[v] );}}if( root[T] == ori ) { printf( "-1\n" ); return; }printf( "%d\n", sgt :: hash1[root[T]] ); //hash1即为最后结果 顺便可以起hash作用for( int i = T;i ^ lst[S];i = lst[i] ) ans.push( i );printf( "%d\n", (int)ans.size() );while( ! ans.empty() ) printf( "%d ", ans.top() ), ans.pop();
}int main() {base1[0] = base2[0] = 1;for( int i = 1;i <= MAX;i ++ ) //单纯2^i最后答案hash比较很容易冲撞 再来一个自然溢出hash加强base1[i] = base1[i - 1] * 2 % mod, base2[i] = base2[i - 1] * 17;scanf( "%d %d", &n, &m );for( int i = 1, u, v, w;i <= m;i ++ ) {scanf( "%d %d %d", &u, &v, &w );addedge( u, v, w );addedge( v, u, w );}scanf( "%d %d", &S, &T );dijkstra();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316855.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CF1192B Dynamic Diameter(LCT)

Foreword\text{Foreword}Foreword zhengrunzhe 大佬的矩阵做法过于神奇&#xff0c;蒟蒻无法理解… 欧拉序的做法确实非常巧妙&#xff0c;但我也想不到这么神仙的做法… 提供一种可能简单一些的 LCT 做法。 由于本人 LCT 无法像大佬那么神&#xff0c;讲的会比较详细一些&…

博弈论(基础概念+例题)

博弈论(b站视频) 文章目录一些概念以Nim游戏为例Nim游戏介绍定义 必败/必胜局面必败/必胜局面的判定引理Nim游戏判定引理的等价命题有向图游戏对判定引理的数学描述-Sg函数有向图游戏的和题目&#xff1a;[有向图游戏][有向图游戏的和][构造/转化类]一些概念 以Nim游戏为例 Ni…

.NET Core 3.0 linux 部署小贴士

dotnet core 3.0 目前还是测试版&#xff0c;在linux下安装 sdk 需要有一些注意事项1.下载urlhttps://dotnet.microsoft.com/download/thank-you/dotnet-sdk-3.0.100-preview-009812-linux-x64-binaries2.安装指令mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-3.0.100…

莓良心(第二类斯特林数)

莓良心problemsolutioncodeproblem 莓在执行任务时&#xff0c;收集到了 nnn 份岩浆能源&#xff0c;其中第 iii 份的能量值是 wiw_iwi​ &#xff0c;她 决定将它们分成恰好 kkk 组带回基地&#xff0c;每一组都要有至少 111 份能源。 每一组能源会对运输设备产生负荷值&…

CF1654E Arithmetic Operations(根号分治)

解析 降智谔谔题。 明明都把做法大块想出来&#xff0c;最后很沙雕的一步掉牛角尖里了… 无能狂怒之下写了一发实际复杂度 n2n^2n2 的代码。 不过暴力艹题还是很爽的。 可能确实不太好卡吧&#xff0c;让我自己构造我是卡不掉。 更何况CF应该不会有人和我一样在如此沙雕的地方…

P2575 高手过招

P2575 高手过招 题意&#xff1a; AKN玩游戏玩累了&#xff0c;于是他开始和同伴下棋了&#xff0c;玩的是跳棋&#xff01;对手是wwx&#xff01;这两位上古神遇在一起下棋&#xff0c;使得棋局变得玄幻莫测&#xff0c;高手过招&#xff0c;必有一赢&#xff0c;他们都将用…

PuppeteerSharp: 更友好的 Headless Chrome C# API

前端就有了对 headless 浏览器的需求&#xff0c;最多的应用场景有两个UI 自动化测试&#xff1a;摆脱手工浏览点击页面确认功能模式爬虫&#xff1a;解决页面内容异步加载等问题也就有了很多杰出的实现&#xff0c;前端经常使用的莫过于 PhantomJS 和 selenium-webdriver&…

尽梨了(贪心+dp)

尽梨了problemsolutioncodeproblem 丰之崎学园附近共有 nnn 个商店&#xff0c;在时刻 000 时&#xff0c;英梨梨从学园出发开始购物。 从学园走到任意一个商店&#xff0c;或从一个商店走到另一个商店需要 111 单位时间。 如果英梨梨在时刻 ttt 到达了商店 iii &#xff0c…

CF1612G Max Sum Array

解析 被蓝题虐了。&#xff08;悲 确实不太难&#xff0c;就是没往那边想。 考虑如果某个值的下标分别位 i1,i2,...,ini_1,i_2,...,i_ni1​,i2​,...,in​ 那么如何计算贡献。 每一个下标和前面统计时作为被减数&#xff0c;和后面统计时作为减数&#xff0c;所以 iki_kik​ …

【Acwing 219. 剪纸游戏】

【Acwing 219. 剪纸游戏】 题意&#xff1a; 给定一张 NM 的矩形网格纸&#xff0c;两名玩家轮流行动。 在每一次行动中&#xff0c;可以任选一张矩形网格纸&#xff0c;沿着某一行或某一列的格线&#xff0c;把它剪成两部分。 首先剪出 11 的格纸的玩家获胜。 两名玩家都采取…

中国.NET:东莞+长沙.NET俱乐部现场花絮及合肥、苏州、上海等地活动预

《传承有序》与微软技术的发展历程相似&#xff0c;微软俱乐部的发展经历着沉沉浮浮&#xff0c;曾经随着微软走向封闭与固执&#xff0c;.NET社区年轻一代的声音被忽略&#xff0c;.NET社区后继无人。社区的沉默是可怕的&#xff0c;很多社区沉寂消亡。但是在2018年&#xff0…

团不过(dp)

团不过problemsolutioncodeproblem 由比滨结衣和雪之下雪乃在玩 NimNimNim 游戏。 共有 nnn 堆石子&#xff0c;双方轮流操作&#xff0c;每次可以从一堆非空石子堆中取走任意个石 子&#xff0c;取走最后一个石子的人胜利。 她们决定让由比滨结衣先手&#xff0c;但忘记了每…

【AcWing 235. 魔法珠

【AcWing 235. 魔法珠 题意&#xff1a; 有n堆魔法珠&#xff0c;第i堆有ai个&#xff0c;两个人轮流进行以下操作&#xff1a; 当轮到某人操作时&#xff0c;如果每堆中魔法珠的数量均为 1&#xff0c;那么他就输了。 问谁赢谁输 题解&#xff1a; 经典博弈论问题 注意本…

P5039 [SHOI2010]最小生成树(网络流)

解析 我大受震撼。 真的没想到是网络流。&#xff08;事后诸葛&#xff1a;看几百的数据范围应该试试枚举算法&#xff09; 一道比较开眼界的题吧。 然而用一个十分钟写完的假做法可以骗90分&#xff0c;还是挺香的 首先是一个没什么意思的转化&#xff1a;操作等价于给一条边…

Abp vNext 切换MySql数据库

Abp vNext是Abp的下一代版本&#xff0c;目前还在经一步完善&#xff0c;代码已经全部重写了&#xff0c;好的东西保留了下来&#xff0c;去除了很多笨重的东西&#xff0c;从官宣来看&#xff0c;Abp vNext主要是为了以后微服务架构而诞生的。从源码来看&#xff0c;Abp vNext…

P1199 [NOIP2010 普及组] 三国游戏

P1199 [NOIP2010 普及组] 三国游戏 题意&#xff1a; 有n个武将&#xff0c;每两个武将之间都有默契值&#xff0c;你和电脑玩游戏&#xff0c;轮流选一个。轮到计算机挑选时&#xff0c;它会尝试将对手军队中的每个武将与当前每个自由武将进行一一配对&#xff0c;找出所有配…

P1791 [国家集训队]人员雇佣(网络流)

解析 熟练和固化在有些时候是等价的。 一个看起来喜闻乐见的模型。 n2n^2n2 信息量你在逗我… 结果是&#xff1a;点数 n2n^2n2 TLE&#xff0c;边数 n2n^2n2 AC。 一种之前所没有见过的打开方式。 还是考虑最小割模型&#xff0c;点 iii 向原点连一条 AiA_iAi​ 的边&#xf…

【网络流专练一】UVA五题(UVA12125,UVA11082,UVA10983,UVA1306,UVA10735)

网络流专练March of the Penguins矩阵解压 Matrix DecompressingBuy one, get the rest freeThe K-League混合图的欧拉回路 Euler Circuit什么破网站&#xff0c;多余空格换行都不能有&#xff0c;还nm不报PE/RE只报WA。 少一行换行也不行&#xff0c;这是什么垃圾文本比较。 …

采用.NET CORE的全异步模式打造一款免费的内网穿透工具--NSmartProxy

什么是NSmartProxy&#xff1f;NSmartProxy是一款免费的内网穿透工具。特点跨平台&#xff0c;客户端和服务端均可运行在MacOS&#xff0c;Linux&#xff0c;Windows系统上&#xff1b;使用方便&#xff0c;配置简单&#xff1b;多端映射&#xff0c;一个NSmart Proxy客户端可以…

Acwing 236. 格鲁吉亚和鲍勃(博弈论妙题)

Acwing 236. 格鲁吉亚和鲍勃 题意&#xff1a; 一排网格&#xff0c;将网格从左到右依次编号 1,2,3&#xff0c;…&#xff0c;并将 N 个西洋棋棋子放在不同的网格上&#xff0c;如下图所示&#xff1a; 两个人轮流移动棋子 每次玩家选择一个棋子&#xff0c;并将其向左移动…