1. 程式人生 > 實用技巧 >Luogu5049 旅行(資料加強版)

Luogu5049 旅行(資料加強版)

https://www.luogu.com.cn/problem/P5049

圖論

其實參加過\(NOIP2018\)了,當時\(O(n^2)AC\),但是沒補過這個加強版

首先,對於樹的情況,很容易考慮到把每個節點相鄰的節點從小到大排序,然後從\(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\)排完序之後丟進鏈式前向星中

可能先丟進鏈式前向星,然後取出到陣列中,陣列排序後丟回鏈式前向星更快,沒嘗試

不開\(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;
}