原题:https://www.luogu.org/problemnew/show/P3761
题解:修改一条边,使最长链最短。枚举每一条边,显然可以将树分成两个联通块,那么最长链可以是两个联通块的直径,也可以是,连接两个联通块的中心,即两颗树的半径+枚举的边长。可以用树形dp求。
对于直径:
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);
}
}
#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