[NOIP模拟10.10][线段树维护树的直径]联盟

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值