[Noip2018]旅行(数据加强版)
一.前言
之前有写过原版题目((O(n^2)))的题解,挂一个链接,然后这次肝了一下加强版,加强版题目链接。没有判是不是fa调了一下午qwq……
二.思路
我是标准的部分分写法,只是一个树的话很好做,直接每次贪最小就好,这里不加赘述。
vector<int> q[MAXN];
void dfs60(int x,int fa){
printf("%d ",x);
sort(q[x].begin(),q[x].end());
for(int i=0;i<q[x].size();++i){
if(q[x][i]==fa)continue;
dfs60(q[x][i],x);
}
}
然后是 (m=n) 的基环树。基环树,顾名思义,是基于一个环的树,简单来讲就是将树上两个点连起来,会形成一个环,断掉这个环上任意一边还是树……
定义都不太重要,从中也许能得到n方做法,但是是过不了加强版的(n方移步原版),在加强版中,我选择直接贪心过。在本做法中,关于基环树,只需要知道一个点:我可以反悔一次。具体来讲,就是不走接下来的在环上的另一个点 v,而这个反悔仅限于环上的点 u。
进一步解释,之前是树的时候,我没得选,
必须乖乖的把自己的子树走完。而现在有了环,我可以有一次选择不继续走下去,而是往回走,从环的另一个方向走到本该走的点(下面用图画举例)
也就是从红点到蓝点我有两条路可以走(看箭头),如果我此时反悔不走红线,我还有走蓝色这条线的机会。
明确了这仅有一次的操作后,再来看另一个。
咱现在在红点做出了不走蓝点反悔走父亲灰点的决策,那么在这个决策中,父亲灰点有已经走过的黑点儿子和没有走过的黄点儿子,红点也有没有走过的黄点儿子。
那么在反悔回溯的过程中,我们必须由下至上将所有的黄儿子都走了才行,不然完不成走完所有的要求。
有了上面的铺垫,这道题就可以爆切了。只需要维护一个反悔后第一个要走的黄儿子就行。判断它和接下来要走的蓝点哪个大,如果蓝点更大就反手一个回溯解决问题。关于黄儿子的维护:如果当前点(红点)按照从小到大依次遍历儿子,到了下一个点是在环上的蓝点,且还有儿子没有走(黄点),那么更新维护的黄儿子。特别的,如果没有儿子可以走了,从父亲哪里继承。(代码会清晰一点)
最后涉及到一个找环的问题(可以乱写),我采用深搜找环,压栈的方式,这里看每个人有不同。
三.CODE
int stack[MAXN],top;
bool vis[MAXN],ish[MAXN],fl;
void findh(int x,int fa){
if(fl)return ;
vis[x]=1;stack[++top]=x;//压栈
for(int i=0;i<q[x].size();++i){
if(q[x][i]==fa)continue;
if(vis[q[x][i]]){//有已经遍历到的了,出现环
ish[q[x][i]]=1;
while(top&&stack[top]!=q[x][i]){
ish[stack[top--]]=1;//记录环
}
fl=1;
return ;
}
findh(q[x][i],x);
if(fl)return ;
}
top--;//弹栈
}
int mx=MAXN,alf;//mx是维护的黄儿子
void dfs100(int x,int fa){
vis[x]=1;
printf("%d ",x);
sort(q[x].begin(),q[x].end());//排序以便从小到大
for(int i=0;i<q[x].size();++i){
if(q[x][i]==fa||vis[q[x][i]])continue;//父亲或者一次性不反悔将环走完了
if(!ish[q[x][i]])dfs100(q[x][i],x);//下一个点不在环上维护就是了
else {
if(!alf){//有没有反悔过的标记
int t=((i==q[x].size()-1)?mx:q[x][i+1]);
//取没有遍历过的下一个,没有就继承父亲
if(t==fa)mx=((i==q[x].size()-2)?mx:q[x][i+2]);
//这里特别注意(我调了很久才找到),判断是否是父亲
else mx=t;
}
if(mx<q[x][i]&&!alf){
alf=1;continue;//反悔
}
else dfs100(q[x][i],x);//走就完了
}
}
}
int main(){
n=read();m=read();
for(int i=1,x,y;i<=m;++i){
x=read();y=read();
q[y].push_back(x);
q[x].push_back(y);
}
if(m==n-1)dfs60(1,0);
else{
findh(1,0);//找环
memset(vis,0,sizeof(vis));
dfs100(1,0);//开始旅行
}
return 0;
}