题解 P3304 【[SDOI2013]直径】

破壁人

2018-01-04 19:49:51

Solution

首先求出直径这个简单,只要dfs一遍就行了。 我们再考虑第二问。要求的是所有直径都经过的边数。 所以我们先任意求出一条直径,记录这条直径上的点。 再对每个直径上的点都dfs,求出以这个点为起点的最长链(当然不能包括直径上的其它点),长度用dis[i]表示。 ls[i]表示这个点到直径左端点的距离。 rs[i]表示这个点到直径右端点的距离。(左右随意定) 从左端点开始向右遍历直径,若到了j号点发现,dis[j]=rs[j]就可以跳出循环,因为j的右边的所有边都不可能是必经边。 因为我们找到了一条不经过它们的直径。 那么是不是左端点到j就是必经边了呢?显然不是的。 因为还有可能出现这种情况,即dis[k]=ls[k],这样就变成了k的左边的所有边都不可能是必经边。 所以我们还要类似的从刚才跳出循环的点开始向左寻找这个k点。 那么j到k的所有边就是必经边了。 ```cpp #include<iostream> #include<vector> #include<cstring> using namespace std; vector<int> a[200001],b[200001]; int last[200001],u,v,next[200001]; long long dis[200001],mmm[200001],op; bool vv[200001]; void dfs1(int o,long long p,int q) { if(p>op){op=p;u=o;} for(int i=0;i<a[o].size();i++) if((!vv[a[o][i]])&&(a[o][i]!=q)) { vv[a[o][i]]=true; dfs1(a[o][i],p+b[o][i],o); } } void dfs2(int o,long long p,int q) { last[o]=q; dis[o]=p; if(p>op){op=p;v=o;} for(int i=0;i<a[o].size();i++) if((!vv[a[o][i]])&&(a[o][i]!=q)) { vv[a[o][i]]=true; dfs2(a[o][i],p+b[o][i],o); } } int main() { int n; cin>>n; for(int i=1;i<n;i++) { int x,y,z; cin>>x>>y>>z; a[x].push_back(y); b[x].push_back(z); a[y].push_back(x); b[y].push_back(z); } memset(vv,0,sizeof(vv));op=0; dfs1(1,0,0); memset(vv,0,sizeof(vv));op=0; dfs2(u,0,0); int distance=dis[v]; cout<<dis[v]<<endl; memset(vv,0,sizeof(vv)); for(int i=v;i!=0;i=last[i]) vv[i]=true; for(int i=v;i!=0;i=last[i]) { op=0; dfs1(i,0,0); mmm[i]=op; } int j=v; for(int i=last[v];i!=0;i=last[i]) next[i]=j,j=i; int ans=0; int i; for(i=j;i!=0;i=next[i]) if(dis[v]-dis[i]==mmm[i]) break; for(;i!=0;i=last[i]) { if(dis[i]==mmm[i]) break; ans++; } cout<<ans<<endl; return 0; } ```