【BZOJ4727】Turysta(POI2017)-构造+链表+SCC
测试地址:Turysta
题目大意:给定一张竞赛图,要求求出从每个点出发的,经过点数最多的一条简单路径,输出方案。
做法:本题需要用到构造+链表+SCC。
首先可以证明一张竞赛图必有一条哈密顿路径(反证法),又可以证明一张强连通的竞赛图必有一条哈密顿回路(这个暂时不知道怎么证,不过看了下面的构造方法差不多也能明白)。我们先来找出原图的哈密顿路径。
竞赛图的任意导出子图一定都是竞赛图,所以我们可以递推进行:假设我们已经求出包含到的哈密顿路径,那么对于点:
如果它指向前面求出的哈密顿路径的头,或者被前面求出的哈密顿路径的尾指向,就直接接上去。
否则,我们至少知道点被前面求出的哈密顿路径的头指向,并指向前面求出的哈密顿路径的尾。因此在这条路径中一定存在相邻的两个点,满足前面的点指向,后面的点被指向,那么只要把接在两点之间即可。这样的递推用链表维护是的。
求出了哈密顿路径,我们能断言:这张图的任意强连通分量必定是这条路径上的一个连续段,并且两个不同强连通分量之间的边的联系,只有可能是从路径中靠前的连向路径中靠后的。换句人话来说,就是对竞赛图SCC缩点后得到的一定是一条链。
前面我们说了,一个强连通竞赛图中必然含有哈密顿回路,那么现在问题就变成,如何求出这样的哈密顿回路,求出之后我们就可以很方便地构造出题目中所求的最佳路径了:首先按当前强连通分量的哈密顿回路走,然后贪心走到下一个强连通分量,再按哈密顿回路走……
下面我们给出有哈密顿路的基础上,求哈密顿回路的算法。我们假设已经求出哈密顿路上前个点的哈密顿回路,而后面接着一条链直到第个点,那么:
如果环中存在一个位置使得它被指向,而它在环上的前一个点指向从环中接出的那条链的头,那么就可以把这条链接到环中。
否则,跳过当前点,考虑。
于是我们就解决了这一题,时间复杂度为。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,pre[2010]={0},nxt[2010]={0},head=1,tail=1;
int low[2010],dfn[2010],tim=0,belong[2010],totscc=0;
int st[2010],top=0;
int looppre[2010],loopnxt[2010],nxtscc[2010];
bool g[2010][2010]={0},vis[2010]={0},inst[2010]={0};
void insert(int x,int y)
{
pre[nxt[x]]=y;
nxt[y]=nxt[x];
nxt[x]=y;
pre[y]=x;
}
void calc_path()
{
for(int i=2;i<=n;i++)
{
if (g[i][head])
{
nxt[i]=head;
pre[head]=i;
head=i;
continue;
}
if (g[tail][i])
{
nxt[tail]=i;
pre[i]=tail;
tail=i;
continue;
}
int x=head;
while(!g[i][nxt[x]]) x=nxt[x];
insert(x,i);
}
}
void tarjan(int v)
{
vis[v]=1;
dfn[v]=low[v]=++tim;
st[++top]=v;
inst[v]=1;
int now=top;
for(int i=1;i<=n;i++)
if (g[v][i])
{
if (!vis[i])
{
tarjan(i);
low[v]=min(low[v],low[i]);
}
else if (inst[i]) low[v]=min(low[v],dfn[i]);
}
if (low[v]>=dfn[v])
{
++totscc;
for(int i=now;i<=top;i++)
{
belong[st[i]]=totscc;
inst[st[i]]=0;
}
top=now-1;
}
}
void calc_loop(int &v,int c)
{
int x=v;
if (!nxt[x]||belong[nxt[x]]!=c)
{
looppre[x]=loopnxt[x]=x;
nxtscc[x]=nxt[x];
v=nxt[x];
return;
}
while(x&&belong[x]==c)
{
looppre[x]=pre[x];
if (g[x][v])
{
loopnxt[x]=v;
looppre[v]=x;
break;
}
loopnxt[x]=nxt[x];
x=nxt[x];
}
x=nxt[x];
int now=x;
while(x&&belong[x]==c)
{
int p=v;
bool flag=0;
do
{
if (g[p][now]&&g[x][loopnxt[p]])
{
loopnxt[x]=loopnxt[p];
looppre[loopnxt[p]]=x;
looppre[now]=p;
loopnxt[p]=now;
flag=1;
break;
}
p=loopnxt[p];
}while(p!=v);
if (flag) now=nxt[x];
else loopnxt[x]=nxt[x],looppre[x]=pre[x];
x=nxt[x];
}
int tmp=v;
while(tmp&&belong[tmp]==c)
nxtscc[tmp]=x,tmp=nxt[tmp];
v=x;
}
void calc_ans(int i,bool type)
{
int x=i,cnt=0;
while(x)
{
int v=x;
do
{
if (type) printf("%d ",v);
else cnt++;
v=loopnxt[v];
}while(v!=x);
x=nxtscc[x];
}
if (!type) printf("%d ",cnt);
else printf("\n");
}
void work()
{
int x=head;
while(x) calc_loop(x,belong[x]);
for(int i=1;i<=n;i++)
{
calc_ans(i,0);
calc_ans(i,1);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
for(int j=1;j<=i;j++)
{
int x;
scanf("%d",&x);
if (x) g[j][i+1]=1;
else g[i+1][j]=1;
}
calc_path();
tarjan(head);
work();
return 0;
}