题解 P5049 【[NOIP2018 提高组] 旅行 加强版】
旅行加强版可以用vector插排900ms卡过
当m=n-1 时
首先讲一下
此时这个图就是一棵树
所以图(树)上路径是唯一的
先建一颗以
预处理每个节点的子节点的顺序,可以用数组排序,但是毕竟是加强版的数据,
所以可以用优先队列处理
优先队列只维护最小值,这与求最小字典序的基本思路臭味相投不谋而合
那么
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int N=5e5+1;
int n,m,u,v;
vector<int> g[N];
priority_queue<int> q[N];
inline int read(){
int s=0;
char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s;
}
void minson(int u,int fa){//处理每个节点子节点的当前最小子节点
for(register int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v==fa)continue;
q[u].push(-v),minson(v,u);
//本人很懒,直接存相反数达到小根堆的效果
}
return;
}
void dfs(int u){
printf("%d ",u);
for(;q[u].size();){//从1开始每个节点都选取最小的子节点,没有就返回
int v=-q[u].top();
q[u].pop();
dfs(v);
}
return;
}
int main(){
n=read(),m=read();
for(register int i=1;i<=m;++i){
u=read(),v=read();
g[u].push_back(v);
g[v].push_back(u);
}
minson(1,-1);
dfs(1);
return 0;
}
愉快の骗了60分
本题重心就在此,前面都是废话:
当m=n 的情况
这样原来的图不再是一棵树,也就是说路径不唯一,这时我们要考虑其他方法
通过手玩数据可以得知这
但是要删去一条边,接下来考虑如何删边
由于
那么我们的首要任务是找出这个环并找到入环点
据说许多神犇和奆佬都会用
判环函数:
//使用时先将根节点1传入并将vis[1]设为true
void circle(int u,int fa){
for(register int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v==fa)continue;
if(vis[v]){//如果这个节点去过了则说明整个环已经被走了一遍了
//v则为入环点,存进huan数组
huan[++p]=v;
huan[++p]=u;
inhuan[u]=inhuan[v]=true;
return;
}
vis[v]=true;
circle(v,u);
if(ok)return;//环存完了直接返回
if(p){//如果存在环
if(u==huan[1]){//如果又回到了入环点
phuan=fa;//记录入环点的父节点
ok=true;
return;
}
//在环里存入这个节点,并直接返回
huan[++p]=u;
inhuan[u]=true;
return;
}
}
return;
}
然后我们分析一下这个环的特点:
先安利一波非常好用の图论编辑器:
图论编辑器传送门
环里有
接下来我们就要对环里的每一个节点的子节点排序
这些子节点不能是入环点的父节点也不能是环里的节点
排序好后怎么断边呢?
首先我们从3这个点进入8后就入环了,我们看看左右哪个点小就往哪里走
如图所示往
往
所以我们就会先遍历
因为遍历完后就要考虑下一次去哪个点,
这么一来
这样就十分简单了,将
再来看这种情况:(也是本人认为最有代表性の栗子)
环里有
接下来我们就要对环里的每一个节点的子节点排序
这些子节点不能是入环点的父节点也不能是环里的节点
我们会发现9这个节点小于11所以我们遍历完
∵
把环上下一个点记作
然后再康康
若有,
若没有,就比较
若
若
对于这个栗子来说,我们遍历完
我们要回到
接下来就是又臭又长简洁明了の代码了:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int N=5e5+1;
bool vis[N],inhuan[N],ok;
int n,m,u,v,cnt,phuan,huan[N],p,du,dv,inf=0x7fffffff;
vector<int> g[N],maxson[N];
priority_queue<int> q[N];
inline int read(){
int s=0;
char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s;
}
void circle(int u,int fa){
for(register int i=0;i<g[u].size();++i){
int v=g[u][i];
if(v==fa)continue;
if(vis[v]){
huan[++p]=v;
huan[++p]=u;
inhuan[u]=inhuan[v]=true;
return;
}
vis[v]=true;
circle(v,u);
if(ok)return;
if(p){
if(u==huan[1]){
phuan=fa;
ok=true;
return;
}
huan[++p]=u;
inhuan[u]=true;
return;
}
}
return;
}
void maxhuanson(){
for(register int i=1;i<=p;++i){
int u=huan[i];
maxson[u].push_back(inf);//先在每个环里节点的子树里添加极值
for(register int j=0;j<g[u].size();++j){
int v=g[u][j];
if(v!=phuan&&!inhuan[v])maxson[u].insert(upper_bound(maxson[u].begin(),maxson[u].end(),v),v);
//二分插排(注意入环节点父亲和环中节点不计入其中)
}
}
return;
}
void delpath(){
int dir,cut;
if(huan[2]<huan[p])dir=1;//判方向
else dir=-1;
if(dir==1){
du=huan[p],dv=huan[1];//如果判断边顺利进行,就直接断最后两边
cut=huan[p];//默认cut为环的另一头(eg_1就是这样的)
for(register int i=1;i<p;++i){
int u=huan[i];
int nxt=i+1;
nxt=huan[nxt];
if(maxson[u][0]){
int cut_p;
cut_p=maxson[u][upper_bound(maxson[u].begin(),maxson[u].end(),nxt)-maxson[u].begin()];
if(cut_p!=inf)cut=cut_p;//如果存在非极值的比nxt大的第一个子节点,更新cut
}
if(nxt>cut){//如果回溯更优,就断nxt与当前节点的连边
du=nxt;
dv=u;
return;
}
}
}
if(dir==-1){//同理
du=huan[2],dv=huan[1];
cut=huan[2];
for(register int i=p;i>2;--i){
int u=huan[i];
int nxt=i-1;
nxt=huan[nxt];
if(maxson[u][0]){
int cut_p;
cut_p=maxson[u][upper_bound(maxson[u].begin(),maxson[u].end(),nxt)-maxson[u].begin()];
if(cut_p!=inf)cut=cut_p;
}
if(nxt>cut){
du=nxt;
dv=u;
return;
}
}
}
return;
}
void minson(int u,int fa){
for(register int i=0;i<g[u].size();++i){
int v=g[u][i];
if((du==u&&dv==v)||(du==v&&dv==u))continue;//除去断边
if(v==fa)continue;
q[u].push(-v),minson(v,u);
}
return;
}
void dfs(int u){
printf("%d ",u);
for(;q[u].size();){
int v=-q[u].top();
q[u].pop();
dfs(v);
}
return;
}
int main(){
n=read(),m=read();
for(register int i=1;i<=m;++i){
u=read(),v=read();
g[u].push_back(v);
g[v].push_back(u);
}
vis[1]=true;
circle(1,-1);
maxhuanson();
delpath();
minson(1,-1);
dfs(1);
return 0;
}
最后就欢乐
(这码量也就算半个业界大毒瘤了)