[TJOI2017]城市 树形dp+树的直径+树的中心

原题:https://www.luogu.org/problemnew/show/P3761

题解:修改一条边,使最长链最短。枚举每一条边,显然可以将树分成两个联通块,那么最长链可以是两个联通块的直径,也可以是,连接两个联通块的中心,即两颗树的半径+枚举的边长。可以用树形dp求。

对于直径:

  • 设dp[x][0/1]表示以x为根子树的最大长,和次大长
void getd(int x,int &ans){
	
	for(int i=h[x];i;i=data[i].nxt){
		int y=data[i].to;int c=data[i].w;if(vis[y]) continue;
		vis[y]=1;getd(y,ans);
		if(dp[y][0]+c>dp[x][0]){//最大链 
			dp[x][1]=dp[x][0];son[x]=y;
			dp[x][0]=dp[y][0]+c;
		}else if(dp[y][0]+c>dp[x][1]){//次大链
			dp[x][1]=dp[y][0]+c;
		} 
	} 
	ans=max(ans,dp[x][0]+dp[x][1]);
}

 对于中心和半径有:

  • from[x]表示除以x为根的子树外,其他点到x的最长距离
  • 从根顺着推
void getr(int x,int from,int &ans){
	ans=min(ans,max(dp[x][0],from));
	for(int i=h[x];i;i=data[i].nxt){
		int y=data[i].to;int c=data[i].w;
		if(!vis[y]) continue;
		vis[y]=0;
		if(son[x]==y) getr(y,max(dp[x][1],from)+c,ans);
		else getr(y,max(dp[x][0],from)+c,ans);
	}
}
#include
#define inf (1<<30)-1
using namespace std;
const int N=5500;
int dp[N][2];
int n,len=1;
struct E{
	int to,w,nxt;
}data[N<<1];
int u[N],v[N],val[N],h[N],son[N];
bool vis[N];
int d1,d2,r1,r2,ans;
inline int rd(){
	int x=0;int f=1;char s=getchar();
	while(!isdigit(s)) f=(s=='-'?-1:f),s=getchar();
	while(isdigit(s)) x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return x*f;
}
inline void ins(int x,int y,int w){
	data[++len].to=y;data[len].w=w;data[len].nxt=h[x];h[x]=len;
	data[++len].to=x;data[len].w=w;data[len].nxt=h[y];h[y]=len;
}
inline void clear(){
	memset(dp,0,sizeof dp);
	memset(vis,0,sizeof vis); 
	memset(son,0,sizeof son);
} 

void getd(int x,int &ans){
	
	for(int i=h[x];i;i=data[i].nxt){
		int y=data[i].to;int c=data[i].w;if(vis[y]) continue;
		vis[y]=1;getd(y,ans);
		if(dp[y][0]+c>dp[x][0]){//最大链 
			dp[x][1]=dp[x][0];son[x]=y;
			dp[x][0]=dp[y][0]+c;
		}else if(dp[y][0]+c>dp[x][1]){
			dp[x][1]=dp[y][0]+c;
		} 
	} 
	ans=max(ans,dp[x][0]+dp[x][1]);
}
void getr(int x,int from,int &ans){
	ans=min(ans,max(dp[x][0],from));
	for(int i=h[x];i;i=data[i].nxt){
		int y=data[i].to;int c=data[i].w;
		if(!vis[y]) continue;
		vis[y]=0;
		if(son[x]==y) getr(y,max(dp[x][1],from)+c,ans);
		else getr(y,max(dp[x][0],from)+c,ans);
	}
}
int main(){
	//freopen("city.in","r",stdin);
	n=rd();
	for(int i=1;i

你可能感兴趣的:(treedp,dp)