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;
}
posted @ 2020-09-08 19:44  GK0328  阅读(155)  评论(0编辑  收藏  举报