发现大部分题解都是O(n^2)的复杂度,这里分享一个O(n)复杂度的方法。

题目传送

  首先前60%的情况,图是一棵无根树,只要从1开始DFS,每次贪心走点的编号最小的点就行了。(为什么?因为当走到一个点u时,若不把以它为根的子树的所有点都遍历一遍的话,回溯到u的父亲后,就再也没可能遍历u的没有遍历过的儿子了。)

  再看剩下40%的情况,由于题目保证图是一个无向连通图,当 边数 等于 点数减一 时图必为树,在此基础上再多加一条边,就在一棵树的基础上形成一个环(为了方便,后文仍会提到树,而后文的树指的是图没有第n条边时形成的树)。有了环会发生什么?发现有了环后第一种情况的贪心+DFS解法的依据就不成立了,即走到一个点u时,即使不把以它为根的子树的所有点都遍历一遍,当回溯到u的父亲后,也有可能会通过环的一部分到达u节点剩下的没有遍历过的儿子。显然不能再无脑贪心了。  

  仔细思考一下两种情况的不同,发现若一棵子树中没有环,也没有点能直接连向环,那这棵子树就可以用第一种情况的贪心+DFS的方法处理。若一个点u及它的儿子v都在环上,那么若要u走到v,既可以直接走u到v的连边(u,v),也可以从u开始反方向绕环一圈走到v;若u和v至少有一个点不在环上,那么从u到v只能通过边(u,v),即只有一个到达方法。这就说明,若一对父子都在环上,那他们之间有两种到达方法;否则就只有一种到达方法。

  这时两种情况的不同就明确了:同样的是:对于一个与环没有什么关系的子树(没有关系指不与环的任何一个边相交。若与环共用最多一个点,也没事。),用贪心+DFS做就好,因为当父亲回溯后未被遍历的儿子就不能再被遍历到了;不同的是,第二种情况多了父子都在环上的情况,这时父亲回溯一次后,儿子仍能被遍历。但因为“小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。”,所以每个父亲最多也只能回溯一次。

  所以我们只要搞清楚第一次环上的回溯何时发生就行了,只要发生了一次环上的回溯,第二种情况就可以当第一种情况做了(一旦环上的某个点u回溯了,那么它的儿子与它的连边就不会再用了,也相当于没有这条边,此时图只有n-1条边,就是棵树)。“环上的回溯”显然只会发生在环上(毕竟名字都说是“环上的”了),这其实就相当于在第一次环上的回溯发生前,环上的点可以“主动”发起回溯,即就算它的儿子还没有都被遍历完,它也可以回溯,不过那个没有被遍历的儿子只能是环上的点。

  思考为什么要主动回溯。我们各种乱搞,不就是为了最后的字典序最小吗?而为了达成这个目标,我们只要保证能遍历到所有点的同时,时刻最小化当前的字典序,即每次都遍历可行的编号最小的点。于是我们可以记录一下主动回溯后可以得到的最小字典序就行啦。先跑一边tarjan找到环。从第一次进入环开始就记录主动回溯后可以得到的最小字典序(sec变量),若当前点u在环上,且只剩一个同在环上的儿子了,并且儿子的编号还大于sec,那就主动回溯;不然就正常dfs就行。

具体实现看代码吧:

  

 #include<iostream>
#include<cstdio>
#include<queue> #define min(a,b) ((a)>(b)?(b):(a)) using namespace std; const int N=; int n,m,x,vis[N],lst[N],xu[N],cntxu,nxt[N<<],to[N<<],cnt;
int dfn[N],dfss,low[N],huan[N],sta[N],top; char ch; inline int read()
{
x=;
ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
} void tarjan(int u,int fa)
{
dfn[u]=low[u]=++dfss;
sta[++top]=u;
int Top=top;
vis[u]=;
int t;
for(int e=lst[u];e;e=nxt[e])
{
if(!dfn[t=to[e]])
{
tarjan(t,u);
low[u]=min(low[t],low[u]);
}
else
{
if(t!=fa&&vis[t])
low[u]=min(low[t],low[u]);
}
}
if(dfn[u]==low[u])
{
if(Top==top)
vis[sta[top--]]=;
for(int i=Top;i<=top;++i)
{
huan[sta[i]]=;//是环
vis[sta[i]]=;
}
top=Top-;
}
} int fir;//有没有进过环
int sec=-;//-1标记意义为还没有进入过环 void dfs(int u)
{
if(vis[u]) return;
priority_queue<int,vector<int>,greater<int> >hep;//用堆维护当前要dfs的最小值。由于每个节点的儿子都很少,所以时间复杂度为几乎可以忽略的常数
xu[++cntxu]=u;//记录答案序列
vis[u]=;
for(int e=lst[u];e;e=nxt[e])
if(!vis[to[e]])
hep.push(to[e]);
int head;
if(huan[u]&&!fir)
{
fir=;
while(!hep.empty())
{
head=hep.top();
hep.pop();
if(!huan[head]) dfs(head);//不在环上的点正常贪心DFS。
else
{
if(!vis[head]&&sec==-)
{
sec=hep.top();
dfs(head);
}
else//第一次环上回溯发生后,都正常贪心DFS
dfs(head);
}
}
}
else
{
if(!huan[u]||(huan[u]&&sec==-))
{
while(!hep.empty())
{
if(!vis[hep.top()])
dfs(hep.top());
hep.pop();
}
}
else
{
while(!hep.empty())
{
head=hep.top();
hep.pop();
if(!huan[head])
dfs(head);
else
{
if(head<=sec)
{
if(!hep.empty())
sec=hep.top();
dfs(head);
}
else
{
if(hep.empty())
{
sec=-;//主动回溯,并把sec设成-2标记第一次环上的回溯已经结束了
return;
}
else//在环上的点,要没有 不在环上的儿子 时才能考虑主动回溯
{
sec=hep.top();
dfs(head);
while(!hep.empty())
{
dfs(hep.top());
hep.pop();
}
} }
}
}
}
}
} inline void addedge(int u,int v)
{
nxt[++cnt]=lst[u];
lst[u]=cnt;
to[cnt]=v;
} int main()
{
n=read(),m=read();
int u,v;
for(int i=;i<=m;++i)
{
u=read(),v=read();
addedge(u,v);
addedge(v,u);
}
tarjan(,);
dfs();
for(int i=;i<=n;++i)
printf("%d ",xu[i]);
return ;
}

洛谷 P5022 旅行——题解的更多相关文章

  1. 洛谷P5022 旅行 题解 去环/搜索

    题目链接:https://www.luogu.org/problem/P5022 这道题目一开始看的时候没有思路,但是看到数据范围里面有一个: \(m = n-1\) 或 \(m = n\) ,一下子 ...

  2. 洛谷P5022 旅行 题解

    前面几个代码都是部分分代码,最后一个才是AC了的,所以最后一个有详细注释 安利一发自己的Blog 这是提高组真题,233有点欧拉回路的感觉. 题目大意: 一个 连通 图,双向边 ,无重边 , 访问图中 ...

  3. 洛谷 P5022 旅行

    今天换标题格式了,因为感觉原版实在有点别扭…… 还是直接上题板,看完题再讲吧: 对了有个小细节没说,m一定是等于n或者等于n-1的. 这题是2018年提高组的真题哦!被我肝了2天肝出来了,2天……(真 ...

  4. 洛谷NOIp热身赛题解

    洛谷NOIp热身赛题解 A 最大差值 简单树状数组,维护区间和.区间平方和,方差按照给的公式算就行了 #include<bits/stdc++.h> #define il inline # ...

  5. 洛谷P2827 蚯蚓 题解

    洛谷P2827 蚯蚓 题解 题目描述 本题中,我们将用符号 ⌊c⌋ 表示对 c 向下取整. 蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓. 蛐蛐国里现 ...

  6. 洛谷P1816 忠诚 题解

    洛谷P1816 忠诚 题解 题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意.但是由于一些人 ...

  7. [POI 2008&洛谷P3467]PLA-Postering 题解(单调栈)

    [POI 2008&洛谷P3467]PLA-Postering Description Byteburg市东边的建筑都是以旧结构形式建造的:建筑互相紧挨着,之间没有空间.它们共同形成了一条长长 ...

  8. [NOI 2020 Online] 入门组T1 文具采购(洛谷 P6188)题解

    原题传送门 题目部分:(来自于考试题面,经整理) [题目描述] 小明的班上共有 n 元班费,同学们准备使用班费集体购买 3 种物品: 1.圆规,每个 7 元. 2.笔,每支 4 元. 3.笔记本,每本 ...

  9. [洛谷P3948]数据结构 题解(差分)

    [洛谷P3948]数据结构 Description 最开始的数组每个元素都是0 给出n,opt ,min,max,mod 在int范围内 A: L ,R ,X 表示把[l,R] 这个区间加上X(数组的 ...

随机推荐

  1. excel常用公式--计算统计类

    Count/Countif/Countifs:条件计数. 注:count只能对数值进行统计 sum/sumif/sumifs:条件求和.   Average/Averageifs:  返回参数的平均值 ...

  2. 【转帖】Linux系统上面qemu 模拟arm

    零基础在Linux系统搭建Qemu模拟arm https://blog.csdn.net/weixin_42489042/article/details/81145038 自己没搞定 改天再试试 感谢 ...

  3. Codeforces Round #589 (Div. 2) (e、f没写)

    https://codeforces.com/contest/1228/problem/A A. Distinct Digits 超级简单嘻嘻,给你一个l和r然后寻找一个数,这个数要满足的条件是它的每 ...

  4. 【提高组NOIP2008】双栈排序 (twostack.pas/c/cpp)

    [题目描述] Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈 ...

  5. [LeetCode] 完全二叉树的节点个数

    题目链接: https://leetcode-cn.com/problems/count-complete-tree-nodes 难度:中等 通过率:57.4% 题目描述: 给出一个 完全二叉树 ,求 ...

  6. day 17 模块

    模块是什么? 抖音: 20万行代码全部放在一个py文件中? 为什么不行? 1. 代码太多,读取代码耗时太长. 代码不容易维护. 所以我们怎么样? 一个py文件拆分100文件,100个py文件又有相似相 ...

  7. 11、权重残差图、RLE和NUSE

    affyPLM包可以对芯片原始数据进行拟合回归,最后得到芯片权重(Weights)残差(Residuals)图.相对对数表达(RLE,Relative log expression)箱线图.相对标准差 ...

  8. 如何把maven文件pom.xml中的java包下载下来

    右击pom.xml文件,选择Run As-->Maven build- 在打开的页面中,如图输入"dependency:copy-dependencies",后点击" ...

  9. jsp页面中JSTL如何处理日期格式

    引入fmt标签 <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%> 格 ...

  10. 使用Lombok来优雅的编码

    介绍在项目中使用Lombok可以减少很多重复代码的书写.比如说getter/setter/toString等方法的编写. IDEA中的安装打开IDEA的Setting –> 选择Plugins选 ...