题目给一棵树,边带有权值,求每一点到其他点路径上的最大权和。
树上任意两点的路径都可以看成是经过某棵子树根的路径,即路径权=两个点到根路径权的和,于是果断树分治。
对于每次分治的子树,计算其所有结点到根的距离;对于每个结点,找到另一个离根最远的且与该结点路径过根的结点,二者的距离和就是这个点在过这棵子树的根能到的最远距离。
现在问题就是怎么比较快地找到这另一个最远距离的点。。两点路径过根,说明两点间不存在一点是另一点的祖先。。我一开始还想用DFS序+线段树来着。。想了想,想出了线性的算法:
记录每个结点属于根的哪个儿子,把当前分治子树的所有结点以属于根的哪个儿子分组,对于每个结点对应过根的结点就不能在同一组要到别的组找,而且肯定是某个组里面根距离最大的值的那个结点。通过计算每个组里面结点到根的最大值作为组的值,以及这些组的最大值和次大值,就OK了。
好难描述= =反正这样这题就用树分治解决了,时间复杂度O(nlogn)。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define INF (1<<29) 6 #define MAXN 33333 7 struct Edge{ 8 int u,v,w,next; 9 }edge[MAXN<<1]; 10 int NE,head[MAXN]; 11 void addEdge(int u,int v,int w){ 12 edge[NE].u=u; edge[NE].v=v; edge[NE].w=w; 13 edge[NE].next=head[u]; head[u]=NE++; 14 } 15 16 bool vis[MAXN]; 17 18 int size[MAXN]; 19 void getSize(int u,int fa){ 20 size[u]=1; 21 for(int i=head[u]; i!=-1; i=edge[i].next){ 22 int v=edge[i].v; 23 if(vis[v] || v==fa) continue; 24 getSize(v,u); 25 size[u]+=size[v]; 26 } 27 } 28 int centre,mini; 29 void getCentre(int u,int fa,int &tot){ 30 int res=tot-size[u]; 31 for(int i=head[u]; i!=-1; i=edge[i].next){ 32 int v=edge[i].v; 33 if(vis[v] || v==fa) continue; 34 getCentre(v,u,tot); 35 res=max(res,size[v]); 36 } 37 if(mini>res){ 38 mini=res; 39 centre=u; 40 } 41 } 42 int getCentre(int u){ 43 getSize(u,u); 44 mini=INF; 45 getCentre(u,u,size[u]); 46 return centre; 47 } 48 49 int n,ans[MAXN]; 50 int dn,dx[MAXN],dy[MAXN],mx1,mx2,belong[MAXN],mx[MAXN]; 51 void dfs(int u,int fa,int dist,int &gp){ 52 dx[dn]=u; dy[dn]=dist; dn++; 53 belong[u]=gp; 54 mx[gp]=max(mx[gp],dist); 55 for(int i=head[u]; i!=-1; i=edge[i].next){ 56 int v=edge[i].v; 57 if(vis[v] || v==fa) continue; 58 dfs(v,u,dist+edge[i].w,gp); 59 } 60 } 61 void conquer(int u){ 62 dn=0; mx1=0; mx2=-INF; 63 dx[dn]=u; dy[dn]=0; dn++; 64 belong[u]=u; 65 for(int i=head[u]; i!=-1; i=edge[i].next){ 66 int v=edge[i].v; 67 if(vis[v]) continue; 68 mx[v]=-INF; 69 dfs(v,u,edge[i].w,v); 70 if(mx1<mx[v]) mx2=mx1,mx1=mx[v]; 71 else if(mx2<mx[v]) mx2=mx[v]; 72 } 73 for(int i=0; i<dn; ++i){ 74 if(mx[belong[dx[i]]]!=mx1) ans[dx[i]]=max(ans[dx[i]],dy[i]+mx1); 75 else ans[dx[i]]=max(ans[dx[i]],dy[i]+mx2); 76 } 77 } 78 79 void divide(int u){ 80 u=getCentre(u); 81 vis[u]=1; 82 conquer(u); 83 for(int i=head[u]; i!=-1; i=edge[i].next){ 84 int v=edge[i].v; 85 if(vis[v]) continue; 86 divide(v); 87 } 88 } 89 int main(){ 90 int t,a,b,c; 91 scanf("%d",&t); 92 for(int cse=1; cse<=t; ++cse){ 93 NE=0; 94 memset(head,-1,sizeof(head)); 95 scanf("%d",&n); 96 for(int i=1; i<n; ++i){ 97 scanf("%d%d%d",&a,&b,&c); 98 addEdge(a,b,c); addEdge(b,a,c); 99 } 100 memset(vis,0,sizeof(vis)); 101 for(int i=0; i<n; ++i) ans[i]=-INF; 102 divide(0); 103 printf("Case %d: ",cse); 104 for(int i=0; i<n; ++i) printf("%d ",ans[i]); 105 } 106 return 0; 107 }