题解:P3561 [POI 2017] Turysta
考虑缩点,由于是竞赛图所以缩点后一定是一条链。
先考察答案大小,我们猜测一个竞赛图中的 SCC 一定存在任意点出发的哈密顿路径,于是答案就是把点所在的 SCC 以及这个 SCC 在链上可以抵达的所有 SCC 大小加和。
构造答案等价于对每个点找其所在 SCC 中从其出发的哈密顿路径,为了好做,我们强化下问题,不妨对每个 SCC 直接构造哈密顿回路。
考虑建立 dfs 生成树构造,任选一个点建立 dfs 生成树,观察到树上横叉边一定是 dfs 序大的指向小的,且由于是竞赛图所有横叉边一定存在,于是考虑在 dfs 生成树上按照访问顺序的倒序合并一个点的所有子树的序列,再把这个点本身加入到序列开头,最后根处会得到从根出发的一条哈密顿路径。
但是我们想找的是一条回路,注意到找到的哈密顿路劲上最后一个点是 dfs 生成树上最左边的点(先访问的点放在左边),如果这个点存在到根的返祖边就最好,否则我们找到这个点的返祖边能回到的最浅的点,以这个点作为新根再次 dfs,优先遍历这个点到原先 dfs 生成树上最左边的点间的路径,得到的新 dfs 生成树再按照上面构造哈密顿路径的方法构造,得到的结果便是一条哈密顿回路。
时间复杂度
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3+114;
bool E[maxn][maxn];
int n;
int dfn[maxn],low[maxn],dfncnt;
int col[maxn],cl,sz[maxn];
int vis[maxn],stk[maxn],tp;
bool e[maxn][maxn];
vector<int> seq[maxn];
int F[maxn];
void tarjan(int u){
dfn[u]=low[u]=++dfncnt;
stk[++tp]=u;
vis[u]=1;
for(int v=1;v<=n;v++){
if(E[u][v]==1){
if(dfn[v]==0){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(vis[v]==1){
low[u]=min(low[u],dfn[v]);
}
}
}
if(low[u]>=dfn[u]){
cl++;
F[cl]=u;
while(stk[tp]!=u){
col[stk[tp]]=cl;
sz[cl]++;
vis[stk[tp]]=0;
tp--;
}
col[u]=cl;
vis[u]=0;
sz[cl]++;
tp--;
}
}
int dp[maxn],nxt[maxn];
int use[maxn];
int dep[maxn];
int g[maxn];
int root;
void dfs(int u,int now){
use[u]=1;
int d=0;
for(int v=1;v<=n;v++){
if(col[v]==now&&E[u][v]==1){
if(use[v]==0){
if(g[u]==0) g[u]=v;
d++;
dep[v]=dep[u]+1;
dfs(v,now);
}
}
}
if(d==0&&root==0){
for(int v=1;v<=n;v++){
if(col[v]==now&&E[u][v]==1){
if(root==0||dep[v]<dep[root]) root=v;
}
}
}
}
vector<int> sol(int u,int now){
use[u]=1;
vector<int> res={u};
vector< vector<int> > son;
if(use[g[u]]==0&&g[u]!=0){
son.push_back(sol(g[u],now));
}
for(int v=1;v<=n;v++){
if(col[v]==now&&E[u][v]==1&&use[v]==0&&v!=g[u]){
son.push_back(sol(v,now));
}
}
reverse(son.begin(),son.end());
for(vector<int> x:son){
for(int y:x) res.push_back(y);
}
return res;
}
void build(){
int s=1;
for(int i=2;i<=cl;i++){
if(e[s][i]==1) s=i;
}
dp[s]=sz[s];
for(int i=2;i<=cl;i++){
int t=0;
for(int j=1;j<=cl;j++){
if(dp[j]==0){
t=j;
}
}
for(int j=1;j<=cl;j++){
if(dp[j]==0){
if(e[t][j]==1) t=j;
}
}
dp[t]=sz[t]+dp[s];
nxt[t]=s;
s=t;
}
//构造哈密顿回路
for(int i=1;i<=cl;i++){
if(sz[i]!=1){
root=0;
dfs(F[i],i);
memset(use,0,sizeof(use));
seq[i]=sol(root,i);
}else seq[i].push_back(F[i]);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
cin>>E[j][i];
E[i][j]=E[j][i]^1;
}
}
for(int i=1;i<=n;i++){
if(dfn[i]==0) tarjan(i);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(col[i]!=col[j]) e[col[i]][col[j]]|=E[i][j];
build();
for(int i=1;i<=n;i++){
cout<<dp[col[i]]<<" ";
int t=0;
for(int j=0;j<seq[col[i]].size();j++){
if(seq[col[i]][j]==i) t=j;
}
for(int j=0;j<seq[col[i]].size();j++,t=(t+1)%seq[col[i]].size()) cout<<seq[col[i]][t]<<" ";
int now=nxt[col[i]];
while(now!=0){
for(int x:seq[now]) cout<<x<<" ";
now=nxt[now];
}
cout<<"\n";
}
return 0;
}