Description
G 国周边的n 个小国家构成一个联盟以抵御G 国入侵, 为互相支援, 他们建立了n − 1 条双向通路,
使得任意两个国家可以经过通路相互到达. 当一个国家受到攻击时, 所有其它国家都会沿着最短路径前往这个国 家进行支援,
经过每条通路所需的时间均为1. 定义一个国家的危险程度为 所有国家全部赶到需要的最短时间, 联盟的危险程度为所有国家的危险程度 的最大值.
为了降低危险程度, 联盟决定断开一条通路并任意连接一条通路, 使得 危险程度尽可能小, 并要求改建完成之后任意两个国家可以经过通路互相到
达. 他们决定让你来设计方案, 你需要告知在最优方案中可能断开哪些边, 并给出任意一组最优方案.
Input
第一行一个正整数n. 接下来n − 1 行每行两个正整数, 表示一条ui, vi 之间的边.
Output Format
输出第一行一个整数表示最小危险程度. 第二行一个整数k, 表示可能被断开的边的数量, 接下来k 个数, 表示 可能断开的边的编号,
按升序输出.接下来一行四个正整数表示一组最优方案, 分别表示断开和新建的边
的端点, 只需给出任意一组合法的方案即可.
Sample Input
4
1 2
2 3
3 4
Sample Output
2
2 1 3
3 4 4 2
HINT
对于20% 的数据, n <= 30.
对于40% 的数据, n <= 300.
对于60% 的数据, n <= 3000.
对于100% 的数据, n <= 300000.
题解
考试的时候只会 N 2 N^2 N2暴力…
原本的做法:
枚举哪一条边被删去,删去后会分成两个子树
在两个子树中分别选能使得最大深度最小的点连起来
然后就没有办法优化了…
这样没有利用直径的性质
设两个子树的直径长度分别是 l 1 , l 2 l1,l2 l1,l2
我们会发现答案是
m a x ( l 1 , l 2 , ⌈ l 1 2 ⌉ + ⌈ l 2 2 ⌉ + 1 ) max(l1,l2,\lceil \frac{l1}{2}\rceil+\lceil \frac{l2}{2}\rceil+1) max(l1,l2,⌈2l1⌉+⌈2l2⌉+1)
问题变为动态维护两个子树中的直径
是可以dp做的…但我想学新科技
线段树根据dfs序建树
维护这一段区间里面直径的两个端点
合并的时候直径一定会在这四个端点中产生
用st表求LCA
复杂度 n l o g n nlogn nlogn,常数比较大…
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define lc now<<1
#define rc now<<1|1
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline void pr1(int x){write(x);printf(" ");}
inline void pr2(int x){write(x);puts("");}
inline int _max(int x,int y){return x>y?x:y;}
struct node{int x,y,next;}a[600005];int len,last[300005];
inline void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
int bin[25],mn[600005][21],pos[600005][21],dep[300005],Log[600005],tot[300005];
int df[310000],pa[310000],id;
int first[300005],fac[600005],dfn,n;
void pre_tree_node(int x,int fa)
{
df[++id]=x;pa[x]=id;
fac[++dfn]=x;first[x]=dfn;tot[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa)dep[y]=dep[x]+1,pre_tree_node(y,x),fac[++dfn]=x,tot[x]+=tot[y];
}
}
inline void init()
{
for(int i=1;i<=dfn;i++)mn[i][0]=fac[i],pos[i][0]=i;
for(int i=1;bin[i]<=dfn;i++)
for(int j=1;j+bin[i]-1<=dfn;j++)
{
pos[j][i]=mn[j][i-1]<mn[j+bin[i-1]][i-1]?pos[j][i-1]:pos[j+bin[i-1]][i-1];
mn[j][i]=min(mn[j][i-1],mn[j+bin[i-1]][i-1]);
}
}
inline int RMQ(int u,int v)
{
int K=Log[v-u+1];
return mn[u][K]<mn[v-bin[K]+1][K]?fac[pos[u][K]]:fac[pos[v-bin[K]+1][K]];
}
inline int lca(int x,int y)
{
if(first[x]>first[y])swap(x,y);
return RMQ(first[x],first[y]);
}
struct edge{int x,y;}w[300005];
int a1[300005],ln,minn;
struct seg
{
int x,y;
seg(){}
seg(int _x,int _y){x=_x;y=_y;}
}tr[300005*4];
inline int getdis(int x,int y)
{
int LA=lca(x,y);
return dep[x]+dep[y]-2*dep[LA];
}
int op[5];
seg pushup(seg n1,seg n2)
{
// int ll=4;
op[1]=n1.x;op[2]=n1.y;op[3]=n2.x;op[4]=n2.y;
// ll=unique(op+1,op+1+ll)-(op+1);
int mx=0,u,v;
for(register int i=1;i<=4;i++)for(register int j=i+1;j<=4;j++)
{
int l=getdis(op[i],op[j]);
if(l>=mx)u=op[i],v=op[j],mx=l;
}
return seg(u,v);
}
void buildtree(int now,int l,int r)
{
if(l==r){tr[now]=seg(df[l],df[l]);return ;}
int mid=(l+r)/2;
buildtree(lc,l,mid);
buildtree(rc,mid+1,r);
tr[now]=pushup(tr[lc],tr[rc]);
}
seg fd(int now,int l,int r,int ql,int qr)
{
if(l==ql&&r==qr)return tr[now];
int mid=(l+r)/2;
if(qr<=mid)return fd(lc,l,mid,ql,qr);
else if(mid+1<=ql)return fd(rc,mid+1,r,ql,qr);
else
{
seg u1=fd(lc,l,mid,ql,mid);
seg u2=fd(rc,mid+1,r,mid+1,qr);
return pushup(u1,u2);
}
}
int novis,G,maxpos,mx[300005][2];
void dfs(int x,int fa)
{
mx[x][0]=mx[x][1]=0;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa&&y!=novis)
{
dep[y]=dep[x]+1,dfs(y,x);
if(mx[x][0]<=mx[y][0]+1)swap(mx[x][0],mx[x][1]),mx[x][0]=mx[y][0]+1;
else if(mx[x][1]<mx[y][0]+1)mx[x][1]=mx[y][0]+1;
}
}
}
int length;
void getrt(int x,int fa,int dep)
{
if(!G||max(mx[x][0],dep)<maxpos)G=x,maxpos=max(mx[x][0],dep);
length=max(max(mx[x][0]+mx[x][1],mx[x][0]+dep),length);
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa&&y!=novis)
{
if(mx[x][0]==mx[y][0]+1)getrt(y,x,max(dep+1,mx[x][1]+1));
else getrt(y,x,max(dep+1,mx[x][0]+1));
}
}
}
int main()
{
bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1;
Log[1]=0;for(int i=2;i<=600000;i++)Log[i]=Log[i>>1]+1;
n=read();
for(int i=1;i<n;i++)
{
w[i].x=read();w[i].y=read();
ins(w[i].x,w[i].y);ins(w[i].y,w[i].x);
}
pre_tree_node(1,0);
init();
buildtree(1,1,id);
minn=999999999;int g1,g2,g3,g4;
for(register int i=1;i<n;i++)
{
int x=w[i].x,y=w[i].y;if(dep[x]>dep[y])swap(x,y);//保证x在上
seg u2;
if(pa[y]+tot[y]<=id)u2=pushup(fd(1,1,id,1,pa[y]-1),fd(1,1,id,pa[y]+tot[y],id));
else u2=fd(1,1,id,1,pa[y]-1);
int l2=getdis(u2.x,u2.y);
if(l2>minn)continue;
seg u1=fd(1,1,id,pa[y],pa[y]+tot[y]-1);
int l1=getdis(u1.x,u1.y);
if(l1>minn)continue;
int dis=_max(_max(l1,l2),(l1+1)/2+(l2+1)/2+1);
if(dis<minn)
{
minn=dis;
a1[ln=1]=i;
}
else if(dis==minn)a1[++ln]=i;
}
pr2(minn);
pr1(ln);for(register int i=1;i<ln;i++)pr1(a1[i]);pr2(a1[ln]);
int i=a1[1];
dep[w[i].x]=dep[w[i].y]=0;
novis=w[i].y;G=length=0;maxpos=999999999;dfs(w[i].x,0);
getrt(w[i].x,0,0);
int p1=G,l1=maxpos;
novis=w[i].x;G=length=0;maxpos=999999999;dfs(w[i].y,0);getrt(w[i].y,0,0);
int p2=G,l2=maxpos;
pr1(w[i].x);
pr1(w[i].y);
pr1(p1);
pr1(p2);
return 0;
}