51Nod1863 Travel 主席树 最短路 Dijkstra 哈希
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1863.html
题目传送门 - 51Nod1863
题意
有 n 个城市,有 m 条双向路径连通它们。
每一个城市有一个属性,可能有多个城市有相同的属性。
给定属性的优先级,求一条从 1 到 n 的路径,满足优先级最大的属性在这条路径中出现次数尽量小,在此基础上,优先级次大的出现次数尽量小,以此类推。
问这条路径经过了哪些属性的节点,按照优先级从高到低输出。
$n\leq 10^5,m\leq 5\times 10^5$
题解
首先需要往最短路方向想这题。
每个点的 dis 可以表示成一个按排序表顺序的数量序列,第 i 个元素表示优先级为 i 的属性的出现次数。
每经过一条边,就相当于单点加一,可以用主席树维护。
那么如何判定两个序列的大小?
我们需要找到的是这两个序列的第一个不同元素。
主席树上维护区间哈希值来判断区间是否相同,然后二分即可。
因为需要用主席树维护,后面的状态基于前面的,所以不能 spfa 。
只能 dijkstra 。但是空间复杂度是 $O(m\log n)$ ,会 MLE 。
我们发现每一次修改的时候,由于一个点自身的属性不会变,所以每次修改这个点的时候,一定是从一个状态继承,并修改 其属性的 rank 这个位置上的值,所以每次新建的节点都是一样的,所以直接覆盖到原先建出来的节点上即可,空间复杂度 $O(n\log n)$ 。
堆优化 dijkstra ,时间复杂度 $O(n\log ^2 n)$ 。(比较大小需要一个 log)
我偷了个懒用 set 当堆,居然只跑了不到 1.3s 。
代码
#include <bits/stdc++.h>
#define clr(x) memset((x),0,sizeof (x))
using namespace std;
int read(){
int x=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
const int N=100005;
int n,m;
int lis[N],rk[N],be[N];
vector <int> e[N];
int root[N];
namespace pt{
const int S=N*50;
int ls[S],rs[S],v[S];
int Pow[N],T,mod,cnt;
void build(int &rt,int L,int R){
rt=++cnt;
if (L==R)
return;
int mid=(L+R)>>1;
build(ls[rt],L,mid);
build(rs[rt],mid+1,R);
}
void init(int n,int _T,int _mod){
T=_T,mod=_mod,cnt=0;
for (int i=Pow[0]=1;i<=n;i++)
Pow[i]=1LL*Pow[i-1]*T%mod;
clr(ls),clr(rs),clr(v);
build(root[0],1,n);
}
void update(int prt,int &rt,int L,int R,int x){
if (!rt)
rt=++cnt;
if (L==R)
return (void)(v[rt]=v[prt]+1);
int mid=(L+R)>>1;
if (x<=mid)
update(ls[prt],ls[rt],L,mid,x),rs[rt]=rs[prt];
else
update(rs[prt],rs[rt],mid+1,R,x),ls[rt]=ls[prt];
v[rt]=(1LL*v[ls[rt]]*Pow[R-mid]+v[rs[rt]])%mod;
}
int ADD(int x,int y){
return x+y>=mod?x+y-mod:x+y;
}
int cmp(int r1,int r2,int L,int R,int x=0,int d=0){
if (!r2)
return -1;
if (L==R){
if (x<L||x>R)
d=0;
if (v[r1]+d==v[r2])
return 0;
return v[r1]+d<v[r2]?-1:1;
}
int mid=(L+R)>>1;
if ((L<=x&&x<=mid&&d?ADD(Pow[mid-x],v[ls[r1]]):v[ls[r1]])!=v[ls[r2]])
return cmp(ls[r1],ls[r2],L,mid,x,d);
else
return cmp(rs[r1],rs[r2],mid+1,R,x,d);
}
int query(int rt,int L,int R,int x){
if (L==R)
return v[rt];
int mid=(L+R)>>1;
int ans=0;
if (x<=mid)
ans=query(ls[rt],L,mid,x);
else
ans=query(rs[rt],mid+1,R,x);
v[rt]=(1LL*v[ls[rt]]*Pow[R-mid]+v[rs[rt]])%mod;
return ans;
}
}
struct Node{
int id;
Node(int _id=0){
id=_id;
}
friend bool operator < (Node a,Node b){
int v=pt :: cmp(root[a.id],root[b.id],1,n);
return (v!=0)?(v<0):(a.id<b.id);
}
};
set <Node> S;
int vis[N],del[N];
int main(){
n=read(),m=read();
for (int i=1;i<=n;i++)
rk[lis[i]=read()]=i;
for (int i=1;i<=n;i++)
be[i]=read();
for (int i=1;i<=m;i++){
int a=read(),b=read();
e[a].push_back(b);
e[b].push_back(a);
}
pt :: init(n,233,998244353);
pt :: update(root[0],root[1],1,n,rk[be[1]]);
S.clear();
S.insert(Node(1));
vis[1]=1;
for (int t=n-1;t--&&!S.empty();){;
int x=(*S.begin()).id;
S.erase(S.begin());
del[x]=1;
for (auto y : e[x]){
if (del[y])
continue;
if (pt :: cmp(root[x],root[y],1,n,rk[be[y]],1)<0){
if (vis[y])
S.erase(Node(y));
pt :: update(root[x],root[y],1,n,rk[be[y]]);
S.insert(Node(y));
vis[y]=1;
}
}
}
clr(vis);
for (int i=1;i<=n;i++)
vis[i]=pt :: query(root[n],1,n,i);
for (int i=1;i<=n;i++)
while (vis[i]--)
printf("%d ",lis[i]);
return 0;
}
51Nod1863 Travel 主席树 最短路 Dijkstra 哈希的更多相关文章
- 【CF464E】The Classic Problem(主席树+最短路)
点此看题面 大致题意: 给你一张无向图,每条边的边权为\(2^{x_i}\),求\(s\)到\(t\)的最短路. 最短路 最短路,首先考虑\(Dijkstra\).这里用\(SPFA\)似乎不太好,因 ...
- Codeforces 464E The Classic Problem(主席树+最短路+哈希,神仙题)
题目链接 题意:给出一张 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边连接 \(u_i,v_i\),边权为 \(2^{w_i}\),求 \(s\) 到 \(t\) 的最短路. \( ...
- Codeforces 464E The Classic Problem (最短路 + 主席树 + hash)
题意及思路 这个题加深了我对主席树的理解,是个好题.每次更新某个点的距离时,是以之前对这个点的插入操作形成的线段树为基础,在O(logn)的时间中造出了一颗新的线段树,相比直接创建n颗线段树更省时间. ...
- CodeForces 464E The Classic Problem | 呆克斯歘 主席树维护高精度
题意描述 有一个\(n\)点\(m\)边的无向图,第\(i\)条边的边权是\(2^{a_i}\).求点\(s\)到点\(t\)的最短路长度(对\(10^9 + 7\)取模). 题解 思路很简单--用主 ...
- Codeforces 464E #265 (Div. 1) E. The Classic Problem 主席树+Hash
E. The Classic Problem http://codeforces.com/problemset/problem/464/E 题意:给你一张无向带权图,求S-T的最短路,并输出路径.边权 ...
- 2019.03.09 bzoj5371: [Pkusc2018]星际穿越(主席树)
传送门 题意简述: 给一个序列,对于第iii个位置,它跟[limi,i−1][lim_i,i-1][limi,i−1]这些位置都存在一条长度为111的无向边. 称dist(u,v)dist(u,v) ...
- 【BZOJ3551】【BZOJ3545】 【ONTAK2010】 Peaks (kruskal重构树+主席树)
Description 在\(Bytemountains\)有\(~n~\)座山峰,每座山峰有他的高度\(~h_i~\). 有些山峰之间有双向道路相连,共\(~m~\)条路径,每条路径有一个困难值 ...
- [bzoj3531][Sdoi2014][旅行] (主席树+树链剖分)
Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰. ...
- bzoj3207--Hash+主席树
题目大意: 给定一个n个数的序列和m个询问(n,m<=100000)和k,每个询问包含k+2个数字:l,r,b[1],b[2]...b[k],要求输出b[1]~b[k]在[l,r]中是否出现. ...
随机推荐
- Android视频录制命令screenrecord
不管是教学,还是为了演示,如果能将Android手机(或平板)的屏幕录制成视频文件,那是一件非常酷的事(iOS8已经提供了这一功能,能通过OS X直接在Mac上录制iPad.iPhone的屏幕,win ...
- MySQL按字段排序后取序号
1 前言 项目中排行榜刚好需要查数据库表然后给出编号,方案一,可以按条件查找出来,然后再按数组序号给编号,但是如果要查表出来直接看,就不太够用了:方案二,就是用代码帮忙编号.参考了网上一些代码,然后发 ...
- mybatis xml中不能直接用大于号、小于号要用转义字符
2.使用 <![CDATA[ ]]>标记
- 【进阶3-2期】JavaScript深入之重新认识箭头函数的this(转)
这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://github.com/yygmind/blog/issues/21 上篇文章详细的分析了各种this的情况,看过之后对this的概 ...
- 3、SourceTree通过PUTTY连接GitLab
一.生成公钥和私钥 使用命令行生成(两种生成方式选择一种即可) 1.安装SourceTree打开SourceTree,点击“命令行模式”. 2.输入如下命令生成key“example@example. ...
- 【Java】SpringBoot配置文件读取中文乱码
[问题]在配置文件application.properties中配置一个值含有中文的变量.spring加载配置之后,读取的变量中文部分出现乱码.根据CSDN说的一堆办法,改encoding为UTF-8 ...
- Android UiAutomator2.0
一.环境搭建 JDK(java环境).SDK(adb appt环境),这两个已经不想再叙述了直接看详见--> android studio 安装,下载地址:https://developer.a ...
- 添加按钮 table增加一行 删减按钮 table去掉一行
需求描述:做的一个AA新增功能,同时可以为这个即将新增的AA添加内容,而且AA的内容默认展示一行列表,点击添加按钮后出现下一行列表 解决思路:页面首先展示一个表头和列表的一行,作为默认展示的一行列表, ...
- Niagara 泵阀
1.泵阀系统 2.Niagara 每次在启动workbench的时候安装一下daemon,后启动platform 启动station的时候,当station的status变成Running之后启动
- tensorflow(4):神经网络框架总结
#总结神经网络框架 #1,搭建网络设计结构(前向传播) 文件forward.py def forward(x,regularizer): # 输入x 和正则化权重 w= b= y= return y ...