[NOI2018] 归程(线段树维护并查集的可持久化/kruskal重构树,倍增+dijkstra最短路)

[NOI2018] 归程

  • description
  • solution1
  • code1
  • solution2
  • code

description

题目描述

本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定

魔力之都可以抽象成一个nnn个节点、mmm条边的无向连通图(节点的编号从111nnn)我们依次用 l,al,al,a描述一条边的长度海拔

作为季风气候的代表城市,魔力之都时常有雨水相伴,因此道路积水总是不可避免的。由于整个城市的排水系统连通,因此有积水的边一定是海拔相对最低的一些边

我们用水位线来描述降雨的程度,它的意义是:所有海拔不超过水位线的边都是有积水

Yazid 是一名来自魔力之都的 OIer,刚参加完 ION2018 的他将踏上归程,回到他温暖的家

Yazid 的家恰好在魔力之都的111号节点。对于接下来 Q 天,每一天 Yazid 都会告诉你他的出发点 v,以及当天的水位线 p

每一天,Yazid 在出发点都拥有一辆。这辆车由于一些故障不能经过有积水的边

Yazid 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。

  • 需要特殊说明的是,第二天车会被重置,这意味着:
    • 车会在新的出发点被准备好。
    • Yazid 不能利用之前在某处停放的车。

Yazid 非常讨厌在雨天步行,因此他希望在完成回家这一目标的同时,最小化他步行经过的边的总长度。请你帮助 Yazid 进行计算

本题的部分测试点将强制在线,具体细节请见「输入格式」和「子任务」。

输入格式

从文件 return.in 读入数据

单个测试点中包含多组数据。输入的第一行为一个非负整数 T,表示数据的组数。

接下来依次描述每组数据,对于每组数据:

第一行2个非负整数 n,m,分别表示节点数、边数。

接下来 m行,每行4个正整数 u,v,l,a,描述一条连接节点 u,v 的、长度为 l、海拔为 a 的边。在这里,我们保证 1≤u,vn

接下来一行3个非负数 Q,K,S,其中:Q表示总天数,K∈0,1是一个会在下面被用到的系数,S 表示的是可能的最高水位线。

接下来 Q 行依次描述每天的状况。每行2个整数 v0,p0 描述一天:

  • 这一天的出发节点为 v=(v0+K×lastans−1)modn+1

  • 这一天的水位线为 p=(p0+K×lastans)mod(S+1)

其中 lastans 表示上一天的答案(最小步行总路程)

特别地,我们规定第 1 天 lastans=0

在这里,我们保证 1≤v0≤n,0≤p0≤S

对于输入中的每一行,如果该行包含多个数,则用单个空格将它们隔开。

输出格式

输出到文件 return.out 中。

依次输出各组数据的答案。对于每组数据:

输出 Q 行每行一个整数,依次表示每天的最小步行总路程

样例

样例 1

1
4 3
1 2 50 1
2 3 100 2
3 4 50 1
5 0 2
3 0
2 1
4 1
3 1
3 2
0
50
200
50
150

第一天没有降水,Yazid 可以坐车直接回到家中。

第二天、第三天、第四天的积水情况相同,均为连接 1,2 号节点的边、连接 3,4 号点的边有积水。

对于第二天,Yazid 从 2 号点出发坐车只能去往 3 号节点,对回家没有帮助。因此 Yazid 只能纯靠徒步回家。

对于第三天,从 4 号节点出发的唯一一条边是有积水的,车也就变得无用了。Yazid 只能纯靠徒步回家。

对于第四天,Yazid 可以坐车先到达 2 号节点,再步行回家。

第五天所有的边都积水了,因此 Yazid 只能纯靠徒步回家。

样例 2

1
5 5
1 2 1 2
2 3 1 2
4 3 1 2
5 3 1 2
1 5 2 1
4 1 3
5 1
5 2
2 0
4 0
0
2
3
1

本组数据强制在线。

第一天的答案是 0,因此第二天的 v=(5+0−1)mod5+1=5,p=(2+0)mod(3+1)=2

第二天的答案是 2,因此第三天的 v=(2+2−1)mod5+1=4,p=(0+2)mod(3+1)=2

第三天的答案是 3,因此第四天的 v=(4+3−1)mod5+1=2,p=(0+3)mod(3+1)=3

数据范围与提示

所有测试点均保证 T≤3,所有测试点中的所有数据均满足如下限制:

n≤2×105,m≤4×105,Q≤4×105,K∈0,1,1≤S≤109

对于所有边:l≤104,a≤109

任意两点之间都直接或间接通过边相连。

solution1

  • 考虑没有一条边被淹。那就是000

  • 考虑边都被淹了。那就是问从起点到111的最短路

  • 考虑淹了一些边。没有淹的边可以随便走,相当于是个连通块

    所以就是求从起点所在连通块到111所在连通块的最短路

显然,边的存在跟每次给定的水位线有关,似乎只能在线维护了。

实则不然,如果我们将所有水位线的答案都提前处理出来,那么在线也就是离线了

这就是我们的——可持久化

最短路可以最开始跑一遍dijkstra解决

连通块就是并查集问题,再加上iii所在块到111的最短路,就用线段树维护

具体而言:将所有边的水位从低到高排序,从后往前加入每一条边

对于第iii号版本的线段树,维护的是所有海拔大于等于该边构成的连通块,以及在此时生成的图上,每个点到111的最短路

初始时,每个点自己为一个连通块t[now].fa=l,t[now].ans=dis[l]

一旦合并两个连通块(按秩合并)就在线段树上新建版本,并修改t[now].fa=New_fa

如果新儿子最短路优于父亲最短路,就又新建版本修改t[now].ans=ans

感觉有可能新建了两个版本??NO!

并查集修改是更改儿子v对应的父亲,新建的log⁡\loglog个点是v一路上的

而答案更新是要更新父亲u对应的答案,就又要新建log⁡\loglogu一路上的点

属于同一个版本root[i]

相当于在上一条边对应的版本线段树基础上一共修改2log⁡2\log2log个点,成为新的版本

最后就是询问,直接找比所给水位线严格大于所有高度都存在的版本

就是用upper_bound()找一下而已啦

code1

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 400005
#define int long long
#define Pair pair < int, int >
priority_queue < Pair, vector < Pair >, greater < Pair > > q;
struct node { int l, r, fa, ans, h; }t[maxn * 20];
struct edge { int u, v, len, h; }E[maxn];
vector < edge > G[maxn];
int n, m, Q, K, S, cnt;
int dis[maxn], root[maxn], high[maxn];
bool vis[maxn];void dijkstra() {memset( vis, 0, sizeof( vis ) );memset( dis, 0x7f, sizeof( dis ) );q.push( make_pair( dis[1] = 0, 1 ) );while( ! q.empty() ) {int u = q.top().second; q.pop();if( vis[u] ) continue;vis[u] = 1;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].v, len = G[u][i].len;if( dis[u] + len < dis[v] ) {dis[v] = dis[u] + len;q.push( make_pair( dis[v], v ) );}}}
}void build( int &now, int l, int r ) {now = ++ cnt;if( l == r ) { t[now].fa = l, t[now].ans = dis[l], t[now].h = 0; return; }int mid = ( l + r ) >> 1;build( t[now].l, l, mid );build( t[now].r, mid + 1, r );
}int query( int now, int l, int r, int pos ) {if( l == r ) return now;int mid = ( l + r ) >> 1;if( pos <= mid ) return query( t[now].l, l, mid, pos );else return query( t[now].r, mid + 1, r, pos );
}int find( int rt, int x ) {int now = query( rt, 1, n, x );while( t[now].fa ^ x ) {x = t[now].fa;now = query( rt, 1, n, x );}return now;
}void modify( int &now, int lst, int l, int r, int pos, int New_fa ) {t[now = ++ cnt] = t[lst];if( l == r ) { t[now].fa = New_fa; return; }int mid = ( l + r ) >> 1;if( pos <= mid ) modify( t[now].l, t[lst].l, l, mid, pos, New_fa );else modify( t[now].r, t[lst].r, mid + 1, r, pos, New_fa );
}void update( int &now, int lst, int l, int r, int pos, int ans ) {t[now = ++ cnt] = t[lst];if( l == r ) { t[now].ans = ans; return; }int mid = ( l + r ) >> 1;if( pos <= mid ) update( t[now].l, t[lst].l, l, mid, pos, ans );else update( t[now].r, t[lst].r, mid + 1, r, pos, ans );
}void modify( int now, int l, int r, int pos ) {if( l == r ) { t[now].h ++; return; }int mid = ( l + r ) >> 1;if( pos <= mid ) modify( t[now].l, l, mid, pos );else modify( t[now].r, mid + 1, r, pos );
}signed main() {
//	freopen( "return.in", "r", stdin );
//	freopen( "return.out", "w", stdout );int T;scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ ) G[i].clear();for( int i = 1, u, v, l, a;i <= m;i ++ ) {scanf( "%lld %lld %lld %lld", &u, &v, &l, &a );G[u].push_back( { u, v, l, a } );G[v].push_back( { u, u, l, a } );E[i] = { u, v, l, a };high[i] = a;}dijkstra();sort( E + 1, E + m + 1, []( edge x, edge y ) { return x.h < y.h; } );sort( high + 1, high + m + 1 );int now = m;m = unique( high + 1, high + m + 1 ) - high - 1;cnt = 0;build( root[m + 1], 1, n );for( int i = m;i;i -- ) {root[i] = root[i + 1];while( E[now].h == high[i] ) {int u = find( root[i], E[now].u );int v = find( root[i], E[now].v );if( t[u].fa ^ t[v].fa ) {if( t[u].h < t[v].h ) swap( u, v ); //并查集按(秩)树高合并modify( root[i], root[i], 1, n, t[v].fa, t[u].fa );if( t[v].ans < t[u].ans )update( root[i], root[i], 1, n, t[u].fa, t[v].ans );//新加入的v带来更小的新答案if( t[u].h == t[v].h )modify( root[i], 1, n, t[u].fa );}now --;}}scanf( "%lld %lld %lld", &Q, &K, &S );int v, p, lastans = 0;while( Q -- ) {scanf( "%lld %lld", &v, &p );v = ( v + K * lastans - 1 ) % n + 1;p = ( p + K * lastans ) % ( S + 1 );p = upper_bound( high + 1, high + m + 1, p ) - high;printf( "%lld\n", lastans = t[find( root[p], v )].ans );}}return 0;
}

solution2

将边权按海拔高度从大到小排序,然后kruskal重构树,显然这个重构树是小根堆

所以如果询问海拔小于询问点某个祖先的权值,意味着这个祖先的子树内所有点都是可以开车到达的,那么其实就是询问这个祖先子树内的点到111的最短路

怎么找个祖先,就可以用倍增来找了

code

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 400005
#define int long long
#define Pair pair < int, int >
vector < int > G[maxn];
priority_queue < Pair, vector < Pair >, greater < Pair > > q;
struct node { int u, v, h; }E[maxn];
int head[maxn], to[maxn << 1], nxt[maxn << 1], len[maxn << 1];
int first[maxn], End[maxn], enxt[maxn];
int T, n, m, cnt, cntt;
int dis[maxn], fa[maxn], val[maxn], ans[maxn];
int f[maxn][20];void addedge( int u, int v, int w ) {to[cnt] = v, nxt[cnt] = head[u], len[cnt] = w, head[u] = cnt ++;
}int find( int x ) { return fa[x] == x ? x : fa[x] = find( fa[x] ); }void dijkstra() {for( int i = 1;i <= n;i ++ ) dis[i] = 1e18;q.push( make_pair( dis[1] = 0, 1 ) );while( ! q.empty() ) {int now = q.top().second, d = q.top().first; q.pop();if( d ^ dis[now] ) continue;for( int i = head[now];~ i;i = nxt[i] )if( dis[to[i]] > dis[now] + len[i] ) {dis[to[i]] = dis[now] + len[i];q.push( make_pair( dis[to[i]], to[i] ) );}}for( int i = 1;i <= n;i ++ ) ans[i] = dis[i];
}void addedge( int u, int v ) {End[cntt] = v, enxt[cntt] = first[u], first[u] = cntt ++;
}void kruskal() {sort( E + 1, E + m + 1, []( node x, node y ) { return x.h > y.h; } );for( int i = 1;i <= n;i ++ ) fa[i] = i;cnt = n;for( int i = 1, tot = 0;i <= m;i ++ ) {int u = E[i].u, v = E[i].v;int fu = find( u ), fv = find( v );if( fu ^ fv ) {++ cnt;val[cnt] = E[i].h;fa[cnt] = fa[fu] = fa[fv] = cnt;addedge( cnt, fu );addedge( cnt, fv );tot ++;if( tot == n - 1 ) break;}}
}void dfs( int u ) {for( int i = 1;i < 20;i ++ )f[u][i] = f[f[u][i - 1]][i - 1];for( int i = first[u];~ i;i = enxt[i] ) {int v = End[i];f[v][0] = u;dfs( v );ans[u] = min( ans[u], ans[v] );}
}void read( int &x ) {x = 0;char s = getchar();while( s < '0' or s > '9' ) s = getchar();while( '0' <= s and s <= '9' ) x = ( x << 1 ) + ( x << 3 ) + ( s ^ 48 ), s = getchar();
}signed main() {freopen( "return.in", "r", stdin );freopen( "return.out", "w", stdout );read( T );while( T -- ) {read( n ), read( m );cnt = cntt = 0;for( int i = 1;i <= n;i ++ ) {head[i] = first[i] = first[i + n] = -1;for( int j = 0;j < 20;j ++ ) f[i][j] = 0;} for( int i = n + 1;i <= ( n << 1 );i ++ ) G[i].clear();for( int i = 1, u, v, l, h;i <= m;i ++ ) {read( u ), read( v ), read( l ), read( h );E[i] = { u, v, h };addedge( u, v, l );addedge( v, u, l );}dijkstra();kruskal();for( int i = n + 1;i <= cnt;i ++ ) ans[i] = 1e18;dfs( cnt );int Q, K, S, v, p, lst = 0;read( Q ), read( K ), read( S );while( Q -- ) {read( v ), read( p );v = ( v + K * lst - 1 ) % n + 1;p = ( p + K * lst ) % ( S + 1 );for( int i = 19;~ i;i -- )if( val[f[v][i]] > p ) v = f[v][i];printf( "%lld\n", lst = ans[v] );}}return 0;
} 

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

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

相关文章

CodeForces - 616D Longest k-Good Segment

CodeForces - 616D Longest k-Good Segment 题意&#xff1a; 有包含n个数的序列a&#xff0c;求能找到最长的区间包含不超过k个不同的元素。 题解&#xff1a; 尺取法&#xff0c;先固定L&#xff0c;然后移动R&#xff0c;R每次移动&#xff0c;当超过k后&#xff0c;L再…

MySQL 集群方案介绍

mysql集群方案这里介绍2种&#xff0c;PXC 和 Replication。大型互联网程序用户群体庞大&#xff0c;所以架构设计单节点数据库已经无法满足需求。大家也深有体会&#xff0c;有一万人在学校网站查成绩或是选课的时候网站时常是访问不了或者相应特别特别慢。这种情况就凸显出来…

模板:回文自动机(PAM)

所谓回文自动机&#xff0c;就是关于回文的自动机。 &#xff08;逃&#xff09; 前言 小清新自动机。 经历过SAM的大风大浪&#xff0c;这个相比而言好理解多了&#xff0c;感觉也许应该先学这个再学SAM… 解析 和trie、AC自动机、SAM等类似的&#xff0c;PAM的每个结点表…

Gym - 215177C 玩游戏

题意&#xff1a; ljcc和他的学妹在玩游戏&#xff0c;这个游戏共有 n 轮&#xff0c;在第 i 轮获胜会获得 i 分&#xff0c;没有平局。 现在给出ljcc和学妹的得分&#xff0c;求是否有一种方案符合当前得分。 题解&#xff1a; 第i轮得到i分&#xff0c;一共n轮&#xff0…

Zju2112 Dynamic Rankings(树状数组套可持久化权值线段树)

Zju2112 Dynamic Rankingsdescriptionsolutioncodedescription 给定一个含有n个数的序列a[1],a[2],a[3]……a[n]&#xff0c;程序必须回答这样的询问&#xff1a;对于给定的i,j,k&#xff0c;在a[i],a[i1 ],a[i2]……a[j]中第k小的数是多少(1≤k≤j-i1)&#xff0c;并且&…

ML.NET案例详解:在.NET下使用机器学习API实现化学分子式数据格式的判定

半年前写过一篇类似的文章&#xff0c;题目是&#xff1a;《在.NET中使用机器学习API&#xff08;ML.NET&#xff09;实现化学分子式数据格式的判定》&#xff0c;在该文中&#xff0c;我介绍了化学分子式数据格式的基本知识&#xff0c;同时给出了一个案例&#xff0c;展示了如…

洛谷P4762: [CERC2014]Virus synthesis(PAM)

解析 自己对PAM的理解不够深刻。 最优方案必然是先选择一个偶回文串&#xff0c;递归构造出它的一半。花一步逆序&#xff0c;然后暴力解决剩下的。 这似乎已经依稀出现了某种dp的思路。 考虑如何更好的转移。设计 transxtrans_xtransx​ 表示长度不超过 xxx 一半的最长回文后…

Triangle HDU - 5914

Triangle HDU - 5914 题意&#xff1a; 有长度分别是1到n的n给木棍&#xff0c;问最少拿走几个木棍&#xff0c;使得剩下木棍无法组成三角形 题解&#xff1a; 组不成三角形的恰巧情况就是ab<c&#xff0c;也就是我们要让剩下的木棍&#xff0c;两者之和等于或小于第三个…

CodeForces - 336A Vasily the Bear and Triangle

CodeForces - 336A Vasily the Bear and Triangle 题意&#xff1a; 给你一个点x&#xff0c;现在这个点和原点组成了矩形&#xff0c;让你在x和y轴分别求一个点&#xff0c;与原点构成的三角形&#xff0c;要求矩形在三角形内&#xff0c;点x在斜边上 题解&#xff1a; 这…

数据结构之线段树合并——永无乡,Lomsat gelral,Tree Rotations,Tree Rotations Escape Through Leaf

文章目录[HNOI2012]永无乡Lomsat gelral「POI2011 R2 Day2」旋转树木 Tree RotationsEscape Through Leaf线段树合并与 fhq-treap合并很类似&#xff0c;也是将两个不同根的线段树暴力合并至于时间复杂度&#xff0c;线段树合并一次是可以达到O(n)O(n)O(n)的&#xff0c;但是大…

吉特仓储管理系统--开源2018年源码

应该说今天过完&#xff0c;这个年就算真正意义上的过完了&#xff0c;没有想到的是又是在出差的路上写这样的文章。废话也不多说&#xff0c;写这篇文章主要的目的是想将去年吉特仓储管理系统开发的一个版本源代码开放出来&#xff0c;供各位开发者阅读使用。github 源代码地址…

模板:广义SAM(字符串)

所谓广义SAM&#xff0c;就是更广泛意义下的SAM &#xff08;逃&#xff09; 前言 感觉字符串的理解难度的巅峰还是在SAM&#xff0c;广义SAM只是在套一些特判罢了&#xff0c;并不是太难理解。 可以解决多字符串的子串问题&#xff0c;几乎就是把SAM能做的东西从单串变成了多…

自定义Visual Studio.net Extensions 开发符合ABP vnext框架代码生成插件[附源码]

介绍我很早之前一直在做mvc5 scaffolder的开发功能做的已经非常完善,使用代码对mvc5的项目开发效率确实能成倍的提高,就算是刚进团队的新成员也能很快上手,如果你感兴趣 可以参考 http://neozhu.github.io/MVC5-Scaffolder/#/ https://github.com/neozhu/MVC5-Scaffolder但是m…

QDU-Training-01

QDU-Training-01 题号题目知识点难度CodeForces 76EPoints数论HDU 4608I-number模拟CodeForces 616DLongest k-Good Segment尺取法Gym 215177C玩游戏思维题HDU 5914Triangle构造题CodeForces 336AVasily the Bear and Triangle思维题 同时纪念我第700篇文章&#xff0c;我还是…

CF1131 G. Most Dangerous Shark (单调栈优化dp)

文章目录problemsolutioncodeproblem solution dpi:dp_i:dpi​: 前iii个多米诺骨牌全都倒下的最小花费 li,ril_i,r_ili​,ri​分别表示第iii个多米诺骨牌倒下时所能波及到的最左/右位置 往左倒&#xff0c;则[li,i)[l_i,i)[li​,i)内的牌都可以选择性地先推倒 dpimin⁡{dpjcos…

洛谷P1650:田忌赛马(贪心)

解析 其实并不简单的一道题。 是刘汝佳老师的例题&#xff0c;搜到之后按照讲的策略写了一发。 &#xff08;由于这个策略并不完全正确&#xff0c;就不展开讲了&#xff09; 好啊&#xff01; 可是感觉讲的策略特别对&#xff0c;为什么呢&#xff1f; 原因在于&#xff0…

CF407 E. k-d-sequence

文章目录problemsolutioncodeproblem solution 特判d0d0d0&#xff0c;相当于寻找最长的一段数字相同的区间 如果要满足公差为ddd等差序列 区间内每个数在模ddd意义下同余每个数互不相同 算法流程 先将序列分成若干个同余mmm的子区间 从左往右扫一遍 对于同余的子区间&…

Monkey and Banana HDU - 1069

Monkey and Banana HDU - 1069 题意&#xff1a; 有n种类型的砖块&#xff0c;每种类型的砖块都有无限个。第i块砖块的长宽高分别用xi&#xff0c;yi&#xff0c;zi来表示。 同时&#xff0c;由于砖块是可以旋转的&#xff0c;每个砖块的3条边可以组成6种不同的长宽高。 在构…

EFCore Lazy Loading + Inheritance = 干净的数据表 (二)

前言本篇是上一篇EFCore Lazy Loading Inheritance 干净的数据表 &#xff08;一&#xff09; 【献给处女座的DB First程序猿】 前菜 的续篇。这一篇才是真的为处女座的DB First程序猿准备的正餐。继续上一篇的话题&#xff0c;我们希望用EFCore&#xff0c;且继续使用与逻辑…

洛谷P7361:拜神(SA、二分、主席树、启发式合并)

解析 很好的一道SA的题。&#xff08;觉得完全可以评黑了啊qwq&#xff09; 我一开始拿SAM和线段树硬做&#xff0c;不断修正最后发现自己无法在可接受复杂度内解决的问题&#xff0c;直接GG… 垃圾数据还骗到了50分 所以写一道题之前还是要先想仔细了&#xff0c;确定整个流程…