树网的核 NOIP2007 BZOJ-1999

题目链接:BZOJ-1999洛谷P1099

主要思路:

先把树的直径求出来(同时记录直径上的点)(随便选一条就可以),用尺取枚举核(最长的满足的路径)(可以利用DFS序将树的直径连续标记)。预处理出直径上的点不经过直径能到达的最远距离以及每个点到直径的左右端点的距离(我的代码中在尺取中求尺取左端点到直径左端点的距离)。每个核的偏心距即为核的左端点到直径左端的的距离,核的右端点到直径右端点的距离,核内部(不包括两端)的点不经过直径所到达的最大距离这几项的最大值。(核越长明显偏心距会越小)

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define M 500005
using namespace std;
struct E{
	int to,nx,d;
}edge[M<<1];
struct p100 {
	int mxd,mxid,n,s;
	int dis[M];
	int tot,head[M];
	void Addedge(int a,int b,int d){
		edge[++tot].to=b;
		edge[tot].d=d;
		edge[tot].nx=head[a];
		head[a]=tot;
	}
	int pre[M][2];
	void dfs(int now,int fa){
		if(dis[now]>mxd)mxd=dis[now],mxid=now;
		for(int i=head[now];i;i=edge[i].nx){
			int nxt=edge[i].to;
			if(fa==nxt)continue;
			dis[nxt]=dis[now]+edge[i].d;
			dfs(nxt,now);
			pre[nxt][0]=now;
			pre[nxt][1]=edge[i].d;
		}
	}
	int L[M],R[M],T,son[M][2],P[M];
	void redfs(int now,int fa){//使树的直径上的点的DFS序连续 
		L[now]=++T;
		P[T]=now;
		if(son[now][0])redfs(son[now][0],now);
		for(int i=head[now];i;i=edge[i].nx){
			int nxt=edge[i].to;
			if(nxt==fa||nxt==son[now][0])continue;
			redfs(nxt,now);
		}
		R[now]=T;
	}
	int st,ed;
	void find_long(){//求树的直径 
		dis[1]=0;
		dfs(1,-1);
		dis[mxid]=0;
		pre[mxid][0]=-1;
		mxd=0;
		dfs(mxid,-1);//两遍DFS 
		ed=mxid;
		while(pre[mxid][0]!=-1){//求出直径上的点 
			int now=pre[mxid][0];
			son[now][0]=mxid;
			son[now][1]=pre[mxid][1];
			mxid=now;
		}
		st=mxid;
	}
	bool mark[M];
	void reredfs(int now,int fa){//求出直径上每个点不经过直径能到达的最远距离
		mxd=max(dis[now],mxd);
		mark[now]=1;
		for(int i=head[now];i;i=edge[i].nx){
			int nxt=edge[i].to;
			if(nxt==fa||mark[nxt])continue;
			dis[nxt]=dis[now]+edge[i].d;
			reredfs(nxt,now);
		}
	}
	int mx[M];
	void Initdis(int l,int r){
		for(int i=l;i<=r;i++)mark[P[i]]=1;//直径上的点不能走 
		for(int i=l;i<=r;i++){
			mxd=0;
			dis[P[i]]=0;
			reredfs(P[i],-1);
			mx[P[i]]=mxd;
		}
	}
	int ans;
	int rdis[M];
	void get_ans(int ld,int l,int r){
		int res=0;
		res=max(rdis[P[r]],ld);
		for(int i=l+1;i<=r-1;i++)res=max(res,mx[P[i]]);//直径上每个点不经过直径到达的的最远距离取MAX 
		ans=min(ans,res);//求最小偏心距 
	}
	void rulercheck(){
		dis[ed]=0;
		dfs(ed,-1);
		for(int i=1;i<=n;i++)rdis[i]=dis[i];
		Initdis(L[st],L[ed]);
		int r=L[st];
		int d=0,ld=0;//左端点的最远距离一定是到直径左端点的距离,右端点同理 
		for(int l=L[st];l<=L[ed];l++){//尺取 
			while(r<L[ed]&&son[P[r]][1]+d<=s)d+=son[P[r]][1],r++;
			if(d<=s)get_ans(ld,l,r);
			d-=son[P[l]][1];//核的长度 
			ld+=son[P[l]][1];//再尺取中求直径左端点到尺取左端点的距离 
			if(r==L[ed])break; 
		}
	}
	void solve(int n,int s) {
		this->n=n;
		ans=1e9;
		memset(mark,0,sizeof(mark));
		this->s=s;
		for(int i=1; i<n; i++) {
			int a,b,d;
			scanf("%d%d%d",&a,&b,&d);
			Addedge(a,b,d);
			Addedge(b,a,d);
		}
		find_long();//求树的直径 
		redfs(mxid,-1);//将树的直径的DFS连续 
		rulercheck();//尺取核 
		printf("%d\n",ans);
	}
} P100;
int main() {
	int n,s;
	scanf("%d%d",&n,&s);
	P100.solve(n,s);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81902890