1. 程式人生 > >BZOJ.4727.[POI2017]Turysta(哈密頓路徑/回路 競賽圖)

BZOJ.4727.[POI2017]Turysta(哈密頓路徑/回路 競賽圖)

() markdown def clas tps solution blog turn htm

題目鏈接

\(Description\)

給出一個n個點的有向圖,任意兩個點之間有且僅一條有向邊。對於每個點v,求出從v出發的一條經過點數最多,且沒有重復經過同一個點一次以上的簡單路徑。
n<=2000

\(Solution\)

詳細題解在這

競賽圖縮點後得到的拓撲圖一定是一條鏈,因為競賽圖任意兩點前後關系確定,所以只有一種拓撲序列
從前邊強連通分量中的任意一點出來 都可以到達後邊強連通分量的任意一點
因為競賽圖的每個強連通分量一定存在一條哈密頓回路
所以只需要求出每一個強連通分量的哈密頓回路,然後在鏈上走,把每個強連通分量的回路存起來中,最後按拓撲序從後往前輸出即可

//4884kb    9220ms
#include <cstdio> #include <cctype> #include <vector> #include <algorithm> #define gc() getchar() const int N=2005; int n,dfn[N],low[N],id,cnt,bel[N],sk[N],top,nxt[N],dgr[N],pos[N]; bool mp[N][N],ins[N]; std::vector<int> scc[N]; inline int read() { int now=0;register
char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } void Tarjan(int x) { dfn[x]=low[x]=++id, sk[++top]=x, ins[x]=1; for(int i=1; i<=n; ++i) if(mp[x][i]) if(!dfn[i]) Tarjan(i),low[x]=std::min(low[x],low[i]); else
if(ins[i]) low[x]=std::min(low[x],dfn[i]); else ;// if(low[x]==dfn[x]) { ++cnt; do{ bel[sk[top]]=cnt, ins[sk[top]]=0, scc[cnt].push_back(sk[top]); }while(x!=sk[top--]); } } inline bool cmp(const int &a,const int &b){ return dgr[a]<dgr[b]; } void Insert(int x) { sk[++top]=x; for(int i=nxt[x]; i/*necessary(對於單獨一個點)*/&&i!=x; i=nxt[i]) sk[++top]=i; } int main() { n=read(); for(int i=2; i<=n; ++i) for(int j=1; j<i; ++j) mp[j][i]=read(), mp[i][j]=mp[j][i]^1; for(int i=1; i<=n; ++i) if(!dfn[i]) Tarjan(i); for(int l,r,sz,i=1; i<=cnt; ++i) { l=r=scc[i][0], sz=scc[i].size(); for(int tmp,j=1; j<sz; ++j)//從1個點開始 擴展成哈密頓路徑 { tmp=scc[i][j]; if(mp[tmp][l]) nxt[tmp]=l, l=tmp; else if(mp[r][tmp]) nxt[r]=tmp, r=tmp; else { for(int k=l; nxt[k]; k=nxt[k]) if(mp[k][tmp]&&mp[tmp][nxt[k]])//在當前路徑上找 連向tmp同時tmp連向其後邊節點的 { nxt[tmp]=nxt[k], nxt[k]=tmp; break; } } } r=0; for(int j=l; j; j=nxt[j]) if(r) { for(int k=r,las=l; ; las=k,k=nxt[k]) { if(mp[j][k]) { nxt[las]=nxt[l]; if(las!=l) nxt[l]=r; r=k, l=j; break; } if(k==l) break; } } else if(mp[j][l]) r=l, l=j; nxt[l]=r; } for(int i=1; i<=n; ++i)//對每個強連通分量拓撲排序 for(int j=1; j<=n; ++j) if(bel[i]!=bel[j]&&mp[i][j]) ++dgr[bel[j]]; for(int i=1; i<=cnt; ++i) pos[i]=i, dgr[i]/=scc[i].size(); //度數要除以size 因為連向強連通分量i的點會連向i中所有點(競賽圖) 而此時拓撲是要對強連通分量的入度排序 std::sort(pos+1,pos+1+cnt,cmp); for(int i=1; i<=n; ++i) { top=0, Insert(i);//直接加入i這個強連通分量即可,走一遍回路,然後再從終點走到下一個強連通分量 //bel[i]中每個點一定都可以走到下一個強連通分量j,否則bel[i]和j就成了環(同一個強連通分量)了 for(int j=1; j<=cnt; ++j)//按照拓撲序添加scc(路徑) if(dgr[pos[j]]>dgr[bel[i]]) Insert(scc[pos[j]][0]); printf("%d ",top); for(int i=1; i<top; ++i) printf("%d ",sk[i]); printf("%d\n",sk[top]); } return 0; }

BZOJ.4727.[POI2017]Turysta(哈密頓路徑/回路 競賽圖)