[SDOI2013]直径
题目大意:
给你一棵n个结点的带边权的树,求该树直径必经边的个数。
思路:
显然直径必经的所有边的肯定是一个直径上的连续一段。
(若超过一段,则出现环,就不是树了)
首先求出原树的任一直径。
预处理出该直径上从结点i出发,不经过直径上其它结点的最长链长度far[i]。
从直径的两端往里缩,如果当前缩到的点i的far[i]等于i到被缩的那一端的距离,就说明直径的这一段至少有两种不重合的情况,肯定不是必经部分。
最后看一下中间没有被缩的部分经过了几个边。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 typedef long long int64; 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const int N=200001; 13 struct Edge { 14 int to,w; 15 }; 16 std::vector<Edge> e[N]; 17 inline void add_edge(const int &u,const int &v,const int &w) { 18 e[u].push_back((Edge){v,w}); 19 e[v].push_back((Edge){u,w}); 20 } 21 bool mark[N]; 22 int from[N],to[N],u,v; 23 int64 dis[N],far[N]; 24 void dfs(const int &x) { 25 for(unsigned i=0;i<e[x].size();i++) { 26 const int &y=e[x][i].to,&w=e[x][i].w; 27 if(y==from[x]) continue; 28 from[y]=x; 29 dis[y]=dis[x]+w; 30 dfs(y); 31 } 32 } 33 void dp(const int &x,const int &par) { 34 for(unsigned i=0;i<e[x].size();i++) { 35 const int &y=e[x][i].to,&w=e[x][i].w; 36 if(y==par||mark[y]) continue; 37 dp(y,x); 38 far[x]=std::max(far[x],far[y]+w); 39 } 40 } 41 int main() { 42 const int n=getint(); 43 for(register int i=1;i<n;i++) { 44 const int u=getint(),v=getint(),w=getint(); 45 add_edge(u,v,w); 46 } 47 dfs(1); 48 for(register int i=1;i<=n;i++) { 49 if(dis[i]>dis[u]) u=i; 50 } 51 dis[u]=from[u]=0; 52 dfs(u); 53 for(register int i=1;i<=n;i++) { 54 if(dis[i]>dis[v]) v=i; 55 } 56 for(register int x=v;x;x=from[x]) { 57 mark[x]=true; 58 to[from[x]]=x; 59 } 60 for(register int x=v;x;x=from[x]) { 61 dp(x,0); 62 } 63 int l=u,r=v; 64 for(register int x=v;x;x=from[x]) { 65 if(far[x]==dis[v]-dis[x]) r=x; 66 } 67 for(register int x=u;x;x=to[x]) { 68 if(far[x]==dis[x]) l=x; 69 } 70 int len=0; 71 for(register int x=r,i=0;x;x=from[x],i++) { 72 if(x==l) len=i; 73 } 74 printf("%lld\n%d\n",dis[v],len); 75 return 0; 76 }