1. 程式人生 > >4727: [POI2017]Turysta 競賽圖相關

4727: [POI2017]Turysta 競賽圖相關

題解:

這題算是競賽圖相關知識的簡單運用了吧。
完成此題,你需要知道:

1、競賽圖都存在一條哈密頓路徑。

這個比較簡單,反證法,假如沒有,設一條最長路徑為 a 1 > a 2

. . . a n 1
> a n a_1->a_2...a_{n-1}->a_n ,考慮一個這條路徑之外的點 p
p

1、 p p 連向 a 1 a_1 ,那麼路徑長度 + 1 +1
2、 a n a_n 連向 p p ,路徑長度 + 1 +1
否則,一定是這樣的:
在這裡插入圖片描述
不難發現,不論怎麼給紅色的邊定向,都會存在 a i > p a_i->p p > a i + 1 p->a_{i+1} ,這樣路徑的長度 + 1 +1 。這樣完成了證明,實際上也是競賽圖哈密頓路徑的構造方法。

2、競賽圖存在哈密頓迴路當且僅當圖強連通。

同樣,這個東西的證明也是可以在構造的過程中完成的。假設我們現在已經得到了一條哈密頓路徑,考慮怎麼把它變成哈密頓迴路(假設其存在,即圖強連通)。設競賽圖有 n n 個點,哈密頓路徑為 a 1 > a 2 . . . a n 1 > a n a_1->a_2...a_{n-1}->a_n ,先找到第一個連回 a 1 a_1 的點 a x a_x ,然後考慮 a x + 1 a_{x+1} 如何讓這個環擴大:
1、 a x + 1 > a 1 a_{x+1}->a_1 ,直接可以擴大。
2、 a y > a x + 1 > a y + 1 a_y->a_{x+1}->a_{y+1} y y 在之前的環上),也可以直接擴大。
否則即環上所有的點與 a x + 1 a_{x+1} 的邊都是連向它的,這時考慮後面(也就是還未加入到環中的鏈上的點)第一個可以連回環上的點 a p a_p (這樣的點一定存在否則圖則不強連通),設 a p a_p 連向環上的 a q a_q ,那麼環可以擴成這樣: a q > > a q 1 > a x + 1 > > a p > a q a_q->繞環->a_{q-1}->a_{x+1}->鏈上其它點(可能沒有)—>a_p->a_q 。不斷重複此過程,即可構造出一條哈密頓迴路。
說了這麼多,回到這題就很簡單了。先Tarjan縮點,競賽圖縮點後一定是一條鏈的樣子,那麼最優的方案一定是每個強連通分量都用這個強連通分量的哈密頓迴路走完,然後再到下一個強連通分量,求出每個強連通分量後求出它的哈密頓迴路即可。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=2010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,mp[Maxn][Maxn];
int low[Maxn],dfn[Maxn],id=0,sta[Maxn],top=0;bool in[Maxn];
int bel[Maxn],cnt=0,sz[Maxn];
vector<int>scc[Maxn];
int nxt[Maxn];
bool vis[Maxn],can[Maxn];
void push(int x)
{
    vis[x]=true;
    for(int i=0;i<scc[cnt].size();i++)
    if(mp[scc[cnt][i]][x])can[scc[cnt][i]]=true;
}
void work()
{
    int st=scc[cnt][0],ed=scc[cnt][0];
    for(int i=1;i<scc[cnt].size();i++)
    {
        int x=scc[cnt][i];
        if(mp[ed][x]){nxt[ed]=x;ed=x;continue;}
        if(mp[x][st]){nxt[x]=st;st=x;continue;}
        for(int y=st;;y=nxt[y])
        if(mp[y][x]&&mp[x][nxt[y]])
        {
            nxt[x]=nxt[y];nxt[y]=x;
            break;
        }
    }
    int end=st;push(st);
    while(end!=ed)
    {
        int x=nxt[end],last=0;
        if(mp[x][st]){end=x;push(x);continue;}
        for(int y=st;;y=nxt[y])
        {
            if(mp[x][y]){nxt[last]=x;nxt[end]=st;end=x;st=y;push(x);break;}
            //y->環->pre[y]->x->y
            if(last==end)
            {
                int newend,t;
                for(int i=0;i<scc[cnt].size();i++)if(!vis[scc[cnt][i]]&&can[scc[cnt][i]]){newend=scc[cnt][i];break;}
                for(int i=st;;i=nxt[i])if(mp[newend][i]){t=i;break;}
                for(int i=nxt[end];;i=nxt[i]){push(i);if(i==newend)break;}
                nxt[end]=st;st=t;end=newend;
                for(int i=st;;i=nxt[i])
                if(nxt[i]==t){nxt[i]=x;break;}
                break;
            }
            last=y;
            //t->環->pre[t]->x->鏈->newend->t
        }
    }
    nxt[ed]