联赛模拟18_联盟
没有用\(O(n)\)的做法,是\(O(nlogn)\)的线段树维护直径 (O(n)的需要维护的信息我觉得又多又乱)
将树的节点按\(dfs\)序建线段树维护联通块的直径
枚举每一条边,查看删掉它之后,剩余的两个联通块合并后的直径 所能取到的 最小的最大值
即 最小的 len=max {\(l_1\),\(l_2\),\(\frac{l_1+1}{2}\) + \(\frac{l_2+1}{2}\) +1}
\(l_1和l_2\) 是两个联通块的直径。
记录\(len\)的最小值及其个数,以及此时的要删的边的编号。
对于找端点,选一条要删的边,输出其端点,然后分别\(dfs\)找到两个联通块直径的中点输出即可
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
using namespace std;
char buf[1<<20],*p1,*p2;
#define L (rt*2)
#define R (rt*2+1)
#define rint register int
#define gc() (p1==p2?(p2=buf+fread(p1=buf,1,1<<20,stdin),p1==p2?EOF:*p1++):*p1++)
#define read() ({\
rint x=0;register bool f=0;register char ch=gc();\
while(!isdigit(ch)) f|=ch=='-',ch=gc();\
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch&15),ch=gc();\
f?-x:x;\
})
const int maxn=3e5+5;
int n;
int cnt;
int Time;
int head[maxn];
int dfn[maxn],ref[maxn];
int dep[maxn],fa[maxn],son[maxn],siz[maxn],top[maxn];
int Min=0x3f3f3f3f;
int mx[maxn][2];
int jilu[maxn];
int G,max_part;
int no_vis;
struct Seg{ int x,y; }t[maxn*4];
struct Edge{ int from,to,next; }e[maxn*2],ed[maxn];
void Aedge(int x,int y){
e[++cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt;
}
void dfs1(int x,int prt){
siz[x]=1,fa[x]=prt,dep[x]=dep[prt]+1;
for(int i=head[x];i;i=e[i].next){
const rint y=e[i].to;
if(y==prt) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(!son[x]||siz[son[x]]<siz[y]) son[x]=y;
}
}
void dfs2(int x,int tp){
top[x]=tp,dfn[x]=++Time,ref[Time]=x;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=e[i].next){
const rint y=e[i].to;
if(y!=son[x]&&y!=fa[x]) dfs2(y,y);
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int cal(int x,int y){
return dep[x]+dep[y]-dep[lca(x,y)]*2;
}
int res[5],xx,yy,max_len;
Seg up(Seg A,Seg B){
Seg ans;
max_len=-1;
res[1]=A.x,res[2]=A.y,res[3]=B.x,res[4]=B.y;
for(int i=1;i<=4;++i){
for(int j=i+1;j<=4;++j){
const rint dis=cal(res[i],res[j]);
if(dis>max_len){
max_len=dis;
xx=res[i];
yy=res[j];
}
}
}
ans=(Seg){xx,yy};
return ans;
}
void build(int rt,int l,int r){
if(l==r) return t[rt].x=t[rt].y=ref[l],void();
const int mid=(l+r)/2;
build(L,l,mid),build(R,mid+1,r);
t[rt]=up(t[L],t[R]);
}
Seg find(int rt,int l,int r,int x,int y){
if(x==l&&r==y) return t[rt];
int mid=(l+r)/2;
if(y<=mid) return find(L,l,mid,x,y);
if(x >mid) return find(R,mid+1,r,x,y);
return up(find(L,l,mid,x,mid),find(R,mid+1,r,mid+1,y));
}
void dfs(int x,int prt){
mx[x][0]=0,mx[x][1]=0;
for(int i=head[x];i;i=e[i].next){
const int y=e[i].to;
if(y==prt||y==no_vis) continue;
dfs(y,x);
if(mx[y][0]+1>mx[x][0]) mx[x][1]=mx[x][0],mx[x][0]=mx[y][0]+1;
else if(mx[y][0]+1>mx[x][1]) mx[x][1]=mx[y][0]+1;
}
}
void find_G(int x,int prt,int dep){
if(!G||max(mx[x][0],dep)<max_part) max_part=max(mx[x][0],dep),G=x;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to;
if(y==prt||y==no_vis) continue;
int tmp;
if(mx[x][0]==mx[y][0]+1) find_G(y,x,max(dep+1,mx[x][1]+1));
else find_G(y,x,max(dep+1,mx[x][0]+1));
}
}
int main(){
freopen("league.in","r",stdin);
freopen("league.out","w",stdout);
n=read();
for(int i=1;i<n;++i){
Aedge(ed[i].from=read(),ed[i].to=read());
Aedge(ed[i].to,ed[i].from);
}
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
for(int i=1;i<n;++i){
int x=ed[i].from,y=ed[i].to;
if(dep[x]>dep[y]) swap(x,y);
Seg a2;
if(dfn[y]+siz[y]<=Time) a2=up(find(1,1,n,1,dfn[y]-1),find(1,1,n,dfn[y]+siz[y],Time));
else a2=find(1,1,n,1,dfn[y]-1);
int len2=cal(a2.x,a2.y);
if(len2>Min) continue;
Seg a1=find(1,1,n,dfn[y],dfn[y]+siz[y]-1);
int len1=cal(a1.x,a1.y);
if(len1>Min) continue;
int d=max(max(len1,len2),(len1+1)/2+(len2+1)/2+1);
if(d<Min){
Min=d;
jilu[jilu[0]=1]=i;
}
else if(d==Min) jilu[++jilu[0]]=i;
}
printf("%d\n%d ",Min,jilu[0]);
for(int i=1;i<=jilu[0];++i) printf("%d ",jilu[i]);
printf("\n");
int x=ed[jilu[1]].from,y=ed[jilu[1]].to;
printf("%d %d ",x,y);
no_vis=y;
max_part=0x3f3f3f3f;
dfs(x,0),find_G(x,0,0);
printf("%d ",G);
no_vis=x;
max_part=0x3f3f3f3f;
dfs(y,0),find_G(y,0,0);
printf("%d\n",G);
return 0;
}