Luogu5049 旅行(数据加强版)
https://www.luogu.com.cn/problem/P5049
图论
其实参加过\(NOIP2018\)了,当时弄了一个\(O(n^2 \log n)AC\),但是没补过这个加强版
本来以为挺简单的,但是一叠细节没有考虑,\(WA\)飞好多次(菜死)
首先,对于树的情况,很容易考虑到把每个节点相邻的节点从小到大排序,然后从\(1\)开始直接遍历
然而题目要的是基环树,如果暴力枚举断边的话,时间复杂度\(O(n^2)\),只能\(AC\)非加强版
那么我们考虑如何判断环上断哪条边(繁琐死了)
假如对于每个节点出度都为\(1\)的环,我们从当前位置向两端\(x,y\)中较小的一个拓展(钦定\(x<y\))
从\(x\)开始不断搜索,直到发现一个\(z>y\),我们停止,从\(y\)反向搜索到\(z\)
\(But,\)每个节点上都挂着一颗树
对于每个节点,它的下一步都有两种选择
\(1.\)向环上搜索(搜下一个环上节点)
\(2.\)向子树搜索(先搜最小的儿子节点)
显然,仍然是取下一个环上节点与最小的儿子节点中较小的一个
要搜索环,也就是我们要判断\(a_i,a_{i+1}\)是否断开
假设满足:
\(a_i\)可以先搜索几颗子树,再搜索\(a_{i+1}\),再搜索剩下的子树
那么可能会有一些节点需要放在\(a_{i+1}\)之后搜索,设其中最小的为\(t(t>a_{i+1})\),搜索完\(a_{i+1}\)后,\(a_{i+2}\)必须\(<t\)才能继续,否则回溯搜索剩余子树显然更优
注意,每个点与之相比较的\(t\)为上一个多余子树\(t\),其他之前的多余子树不必考虑,因为我们当前的搜索已经满足了字典序比它更优
还有一个点,我们本来不是还要反向搜索的吗?如果已经搜索到了\(z\)呢,是否要取呢?
都有可能,倘若上一个多余子树\(t>a_i\),选择\(a_i\)仍然更优,否则仍然断开
这里其实只有一个区别,对于未搜索到\(z\)的\(a_i\),倘若没有上一个多余子树,那么继续搜索更优,对于已搜索到\(z\)的\(a_i\),倘若没有上一个多余子树,那么断开更优
原理和最开始的简单问题相同
时间复杂度:\(O(n \log n)\)(这。。。快排背锅了)
就这样还\(T\)了
我们不用\(vector\)遍历,只好用链式前向星,但是链式前向星没法排序,那么就让\(vector\)排完序之后丢进链式前向星中
可能先丢进链式前向星,然后取出到数组中,数组排序后丢回链式前向星更快
\(update:\)原来跑得那么慢的原因是\(STL\)的\(sort\)!用了一下自己写的快排,从卡的要死到不开\(O2\)最大点\(427ms\)。。。
不开\(O2 \quad AC!\)
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstring>
#define N 500005
using namespace std;
int n,m,x,y,cnt,ct1,ct2,a[N],c[N],f[N],dfn[N],low[N],ans[N];
vector<int>e[N];
stack<int>s;
int tot,fr[N],nxt[N << 1],d[N << 1];
bool h[N];
#define IT vector<int> :: iterator
int read()
{
int S=0;
char c=getchar();
while (c<'0' || c>'9')
c=getchar();
while ('0'<=c && c<='9')
{
S=S*10+c-'0';
c=getchar();
}
return S;
}
void write(int x)
{
if (x>9)
write(x/10);
putchar(x%10+'0');
}
void add(int x,int y)
{
tot++;
d[tot]=y;
nxt[tot]=fr[x];
fr[x]=tot;
}
void tarjan(int u)
{
s.push(u);
dfn[u]=low[u]=++cnt;
for (int i=fr[u];i;i=nxt[i])
{
int v=d[i];
if (v==f[u])
continue;
if (!dfn[v])
{
f[v]=u;
tarjan(v);
low[u]=min(low[u],low[v]);
if (low[v]>=dfn[u])
{
for (c[0]=0;;s.pop())
{
c[++c[0]]=s.top();
if (s.top()==v)
{
s.pop();
break;
}
}
c[++c[0]]=u;
if (c[0]>2)
memcpy(a,c,(c[0]+1)*sizeof(int));
}
} else
low[u]=min(low[u],dfn[v]);
}
}
void rdfs();
void dfs(int u,int F)
{
ans[++ans[0]]=u;
for (int i=fr[u];i;i=nxt[i])
{
int v=d[i];
if (v==F)
continue;
if (ct1==u && ct2==v || ct1==v && ct2==u)
continue;
dfs(v,u);
}
}
void rdfs()
{
int id=0,lst=a[a[0]-1];
for (int i=1;i<a[0];i++)
if (a[i]>lst)
{
id=i;
break;
}
int pr=-1;
if (!id)
{
for (int i=1;i<a[0];i++)
{
for (int j=fr[a[i]];j;j=nxt[j])
if (!h[d[j]] && d[j]!=f[a[i]])
{
if (d[j]<a[i+1])
continue;
pr=d[j];
break;
}
if (pr!=-1 && pr<a[i+1])
{
ct1=a[i+1];
ct2=a[i];
return;
}
}
ct1=a[a[0]],ct2=lst;
} else
{
for (int i=1;i<id;i++)
{
for (int j=fr[a[i]];j;j=nxt[j])
if (!h[d[j]] && d[j]!=f[a[i]])
{
if (d[j]<a[i+1])
continue;
pr=d[j];
break;
}
if (pr!=-1 && pr<a[i+1])
{
ct1=a[i+1];
ct2=a[i];
return;
}
}
while (id<a[0])
{
if (a[id]>pr)
break;
for (int i=fr[a[id]];i;i=nxt[i])
if (!h[d[i]] && d[i]!=f[a[i]])
{
if (d[i]<a[id+1])
continue;
pr=d[i];
break;
}
id++;
}
ct1=a[id-1],ct2=a[id];
}
}
int main()
{
n=read(),m=read();
for (int i=1;i<=m;i++)
{
x=read(),y=read();
e[x].push_back(y);
e[y].push_back(x);
}
for (int i=1;i<=n;i++)
sort(e[i].begin(),e[i].end());
for (int i=1;i<=n;add(i,*e[i].begin()),i++)
for (IT it=e[i].end()-1;it!=e[i].begin();it--)
add(i,*it);
tarjan(1);
if (a[1]>a[a[0]-1])
reverse(a+1,a+a[0]);
for (int i=1;i<=a[0];i++)
h[a[i]]=true;
rdfs();
dfs(1,0);
for (int i=1;i<=ans[0];i++)
write(ans[i]),putchar(' ');
putchar('\n');
return 0;
}