题意:
有一个n个点n条边的树,确定S,T,使得从S出发经过所有点最终到T,记长度为t,使得(t,S,T)的字典序最小
思路:
环套树,破环成树
1.起点终点在同一个子树上,[S,T]上的边只走一次,环上的边只走一次
2.起点和终点在不同的子树上,起点到U,终点到V只走一遍,环上U,V之间只走一遍,其余的边都走两遍
设置一个环上的起点,其余的点到这个点的距离设为dis[x]
则长度为2*n-2+dis[u]-(S,u)-dis[v]-(T,v)或者为2*n-2-circle-dis[u]+dis[v]-(S,u)-(T,v)
分别维护dis[u]-(S,u)的最小值以及-dis[u]-(S,u)的最小值就可以了
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
struct Edge{
int to,next;
}e[N*2];
int head[N],tot,fa[N],vis[N],circle_len;
vector<int>mp;
bool Del[N];
void init(){
tot=0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
memset(Del,false,sizeof(Del));
}
void addedge(int from,int to){
e[tot]=(Edge){to,head[from]};
head[from]=tot++;
}
bool get_circle(int u){
vis[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v==fa[u]) continue;
if(vis[v]){
int x=u;
circle_len=1,mp.push_back(u),Del[u]=true;
while(x!=v) x=fa[x],circle_len++,mp.push_back(x),Del[x]=true;
return true;
}
fa[v]=u;
if(get_circle(v)) return true;
}
return false;
}
int id[N],mx[N];
struct node{
int maxv,id1,id2;
}a[N],ans;
bool check(int x1,int x2,int y1,int y2){
if(x1<y1||(x1==y1&&x2<y2)) return true;
return false;
}
void dfs(int u,int pre,int Belong){
mx[u]=0,id[u]=u;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(Del[v]||v==pre) continue;
dfs(v,u,Belong);
int tmp=mx[v]+1,tmp_id1=id[v],tmp_id2=id[u];
if(tmp_id1>tmp_id2) swap(tmp_id1,tmp_id2);
if(mx[u]+tmp>a[Belong].maxv)
a[Belong].maxv=mx[u]+tmp,a[Belong].id1=tmp_id1,a[Belong].id2=tmp_id2;
else if(mx[u]+tmp==a[Belong].maxv&&check(tmp_id1,tmp_id2,a[Belong].id1,a[Belong].id2))
a[Belong].id1=tmp_id1,a[Belong].id2=tmp_id2;
if(mx[u]<tmp||(mx[u]==tmp&&id[u]>id[v])) mx[u]=tmp,id[u]=id[v];
}
}
int dis[N],n;
//维护dis[u]-(S,u)的最小值以及-dis[u]-(S,u)的最小值就可以了
void solve(int flag){
dis[0]=0;
int tmp_len=-mx[mp[0]],ID=id[mp[0]];
for(int i=1;i<mp.size();i++){
dis[i]=dis[i-1]+1;
int tmp=2*n-2+tmp_len-flag*dis[i]-mx[mp[i]],tmp_id1=id[mp[i]],tmp_id2=ID;
if(flag==-1) tmp-=circle_len;
if(tmp_id1>tmp_id2) swap(tmp_id1,tmp_id2);
if(tmp<ans.maxv||(tmp==ans.maxv&&check(tmp_id1,tmp_id2,ans.id1,ans.id2))) ans.maxv=tmp,ans.id1=tmp_id1,ans.id2=tmp_id2;
if(flag*dis[i]-mx[mp[i]]<tmp_len||(flag*dis[i]-mx[mp[i]]==tmp_len&&id[mp[i]]<ID))
ID=id[mp[i]],tmp_len=flag*dis[i]-mx[mp[i]];
}
}
int main(){
int _;
scanf("%d",&_);
for(int Case=1;Case<=_;Case++){
scanf("%d",&n);
int u,v;
init(),mp.clear();
for(int i=1;i<=n;i++){
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
fa[1]=0;
get_circle(1);
ans.maxv=1e6;
for(int i=1;i<=n;i++)
if(Del[i]){
a[i].maxv=0,dfs(i,0,i);
int tmp=2*n-circle_len-a[i].maxv;
if(tmp<ans.maxv||(tmp==ans.maxv&&check(a[i].id1,a[i].id2,ans.id1,ans.id2)) ) ans=a[i],ans.maxv=tmp;
}
solve(1);
solve(-1);
printf("Case #%d: %d %d %d\n",Case,ans.maxv,ans.id1,ans.id2);
}
return 0;
}