[IOI2013]Dreaming
一道非常类似的题目(link)
试题大意
给你一棵含有$n$个节点的有边权森林,问每次连边将会用$L$的代价,问你若此图通过加边成为树时的最小直径。$n \leq 5\times 10^5$
试题分析
我们可以发现若两棵树要是在合并连接的点一定与树的中心有关。树的中心指对于当$i$为根时,子树上权值和最大的最小。
为什么,应为树的直径的情况只有单独一棵树,两棵树和在一起的,且那时要是合并就是的是树的中心。但其实$CF$那题应该求树的中心也行,因为那是边权都会为$1$.
所以会发现其实应该如果说要把树建完以后会发现是一个菊花树,且根为权重最大的联通块的根。因为若是小的当根的话那么就会多算了一颗树,所以最多只需要算两颗即可。
所以说我们每次处理好中心到叶子节点的最大距离是多少,然后就可以直接去计算答案了。
并且为什么最多只要算到联通块个数$\leq 3$呢,因为刚才说了这是一颗菊花图,所以我们最多有用的其实是两颗子树。
IOI2013
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int N=500001; struct node{ int u,v,w,nex; }x[N<<1]; int cnt,head[N],n,m,l; void add(int u,int v,int w){ x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++; } int dp[N][3],dis[N],col,vis[N],son[N]; /*0最长 1 次长*/ void dfs1(int f,int fath){ vis[f]=1; for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; dfs1(x[i].v,f); if(dp[x[i].v][0]+x[i].w>=dp[f][0]){dp[f][1]=dp[f][0];dp[f][0]=dp[x[i].v][0]+x[i].w,son[f]=x[i].v;} else if(dp[x[i].v][0]+x[i].w>dp[f][1]){dp[f][1]=dp[x[i].v][0]+x[i].w;} } return; } int ans,k; void dfs2(int f,int fath,int Dis){ if(Dis>dp[f][0]){ dp[f][1]=dp[f][0];dp[f][0]=Dis;son[f]=fath; } else if(Dis>dp[f][1]){dp[f][1]=Dis;} ans=max(ans,dp[f][0]);k=min(k,dp[f][0]); for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; if(son[f]==x[i].v) dfs2(x[i].v,f,dp[f][1]+x[i].w); else dfs2(x[i].v,f,dp[f][0]+x[i].w); } } int calc[N]; signed main(){ memset(head,-1,sizeof(head)); n=read(),m=read(),l=read(); for(int i=1;i<=m;i++){ int u=read()+1,v=read()+1,w=read(); add(u,v,w),add(v,u,w); } for(int i=1;i<=n;i++){ if(!vis[i]){ dfs1(i,0); k=INT_MAX; dfs2(i,0,0); calc[++col]=k; } } sort(calc+1,calc+col+1); if(col>=2) ans=max(ans,calc[col]+calc[col-1]+l); if(col>=3) ans=max(ans,calc[col-1]+calc[col-2]+2*l); printf("%d",ans); }
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int N=500001; struct node{ int u,v,w,nex; }x[N<<1]; int cnt,head[N],n,m,l; void add(int u,int v,int w){ x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++; } int dp[N][3],dis[N],col,vis[N],son[N]; /*0最长 1 次长*/ void dfs1(int f,int fath){ vis[f]=1; for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; dfs1(x[i].v,f); if(dp[x[i].v][0]+x[i].w>=dp[f][0]){dp[f][1]=dp[f][0];dp[f][0]=dp[x[i].v][0]+x[i].w,son[f]=x[i].v;} else if(dp[x[i].v][0]+x[i].w>dp[f][1]){dp[f][1]=dp[x[i].v][0]+x[i].w;} } return; } int ans,k,pos; struct Node{ int pos,calc; }S[N<<1]; void dfs2(int f,int fath,int Dis){ if(Dis>dp[f][0]){ dp[f][1]=dp[f][0];dp[f][0]=Dis;son[f]=fath; } else if(Dis>dp[f][1]){dp[f][1]=Dis;} ans=max(ans,dp[f][0]);k=min(k,dp[f][0]); if(dp[f][0]==k) pos=f; for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; if(son[f]==x[i].v) dfs2(x[i].v,f,dp[f][1]+x[i].w); else dfs2(x[i].v,f,dp[f][0]+x[i].w); } } bool cmp(Node x1,Node x2){return x1.calc<x2.calc;} signed main(){ memset(head,-1,sizeof(head)); n=read(),m=read(),l=1; for(int i=1;i<=m;i++){ int u=read(),v=read(),w=1; add(u,v,w),add(v,u,w); } for(int i=1;i<=n;i++){ if(!vis[i]){ dfs1(i,0); k=INT_MAX; dfs2(i,0,0); S[++col].calc=k; S[col].pos=pos; } } sort(S+1,S+col+1,cmp); if(col>=2) ans=max(ans,S[col].calc+S[col-1].calc+l); if(col>=3) ans=max(ans,S[col-1].calc+S[col-2].calc+2*l); printf("%d\n",ans); for(int i=col-1;i>=1;i--) cout<<S[col].pos<<" "<<S[i].pos<<endl; }