bzoj4727 [POI2017]Turysta
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4727
题意:
给出一个n个点的有向图,任意两个点之间有且仅一条有向边。对于每个点v,求出从v出发的一条经过点数最多,
且没有重复经过同一个点两次以上的简单路径。
输入第一行包含一个正整数n(2<=n<=2000),表示点数。接下来n-1行,其中的第i行有i-1个数,如果第j个数是1,那么
表示有向边j->i+1,如果是0,那么表示有向边j<-i+1。
数据范围
2<=n<=2000
题解:
原图给出来是一个竞赛图。
定理:
竞赛图一定存在哈密顿路径
竞赛图存在哈密顿回路 充要条件是强连通。
那么每个强连通分量里的点每个点都可以走完所有的点。
那么这道题就是竞赛图构造哈密顿回路。
然后拓扑序DP。
关于竞赛图如何构造(以及上述两个定理的证明):
在竞赛图中构造哈密顿路径:
假设我们已经构造出了前i个点的哈密顿路径(最初为1),那么加入i+1个点时,会有三种情况:
1、指向首。
2、被尾指向。
这两种之间就加在首位了。
3、找到中间第一个被i+1指向的点,它前一个点与i+1与它重新连接。
已知强连通竞赛图中的哈密顿路径,如何构造哈密顿回路:
首先找到最近的那个指向首的节点i,形成一个环(因为强连通所以一定存在),然后考虑把环扩大,到i+1节点。
1、指向首,就直接扩大即可。
2、指向其中的某个点,走如图的路径形成新环。
3、没有指向环,考虑下一个点。
于是就完成了构造及证明。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int N=2005;
int n,x,g[N][N],to[N][N],dp[N],low[N],dfn[N],inc=0,S[N],top=0,pl[N],cnt=0,q[N],du[N],pre[N],root[N],tot=0,V[N],nxt[N];
bool ins[N];
inline int read()
{
int ret=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-'){w=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar();}
return ret*w;
}
void get()
{
int head=V[1]; int tail=V[1];
if(tot==1) {nxt[tail]=tail; return;}
for(int j=2;j<=tot;j++)
{
int i=V[j];
if(g[i][head]){nxt[i]=head; head=i; continue;}
else if(g[tail][i]){nxt[tail]=i; tail=i; continue;}
int x,y; for(x=nxt[head],y=head;x&&!g[i][x];y=x,x=nxt[x]);
nxt[y]=i; nxt[i]=x;
}
tail=head; head=0;
for(int i=nxt[tail];i;i=nxt[i])
{
if(head)
{
for(int p1=head,p2=tail;;p2=p1,p1=nxt[p1])
{
if(g[i][p1])
{
nxt[p2]=nxt[tail];
if(p2!=tail) nxt[tail]=head;
tail=i; head=p1; break;
}
if(p1==tail) break;
}
}
else if(g[i][tail]){head=tail;tail=i;}
}
nxt[tail]=head;
}
void dfs(int u)
{
inc++; dfn[u]=low[u]=inc;
ins[u]=1; S[++top]=u;
for(int v=1;v<=n;v++) if(g[u][v])
{
if(!dfn[v]) {dfs(v); low[u]=min(low[u],low[v]);}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++; root[cnt]=u; tot=0;
while(1)
{
dp[cnt]++; V[++tot]=S[top];
pl[S[top]]=cnt; ins[S[top]]=0;
top--; if(S[top+1]==u) break;
}
get();
}
}
void print(int x)
{
if(!x){printf("\n");return;}
printf("%d ",x);
for(int i=nxt[x];i!=x;i=nxt[i]) printf("%d ",i);
print(root[pre[pl[x]]]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
for(int j=1;j<=i;j++)
{
x=read();
if(x) g[j][i+1]=1;
else g[i+1][j]=1;
}
for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(g[i][j]&&pl[i]!=pl[j]) to[pl[i]][pl[j]]=1;
for(int i=1;i<=cnt;i++) for(int j=1;j<=cnt;j++) if(i!=j&&to[j][i]) du[i]++;
int lf=1,rg=0; top=0; for(int i=1;i<=cnt;i++) if(du[i]==0) S[++rg]=i;
while(lf<=rg)
{
int x=q[++top]=S[lf]; lf++;
for(int y=1;y<=cnt;y++)
{
if(!to[x][y]||y==x) continue;
du[y]--; if(du[y]==0) S[++rg]=y;
}
}
for(int i=top;i>=1;i--)
{
int x=q[i]; int det=0;
for(int y=1;y<=cnt;y++)
{
if(!to[x][y]||x==y) continue;
if(det<dp[y]){det=dp[y]; pre[x]=y;}
}
dp[x]+=det;
}
for(int i=1;i<=n;i++) {printf("%d ",dp[pl[i]]); print(i);}
return 0;
}