Shortest-path tree
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 130712/130712 K (Java/Others)Total Submission(s): 407 Accepted Submission(s): 125
Problem Description
Given a connected, undirected graph G, a shortest-path tree rooted at vertex v is a spanning tree T of G, such that the path distance from root v to any other vertex u in T is the shortest path distance from v to u in G.
We may construct a shortest-path tree using the following method:
We consider a shortest-path tree rooted at node 1. For every node i in the graph G, we choose a shortest path from root to i. If there are many shortest paths from root to i, we choose the one that the sequence of passing nodes' number is lexicographically minimum. All edges on the paths that we chose form a shortest-path tree.
Now we want to know how long are the longest simple paths which contain K nodes in the shortest-path tree and how many these paths? Two simple paths are different if the sets of nodes they go through are different.
We may construct a shortest-path tree using the following method:
We consider a shortest-path tree rooted at node 1. For every node i in the graph G, we choose a shortest path from root to i. If there are many shortest paths from root to i, we choose the one that the sequence of passing nodes' number is lexicographically minimum. All edges on the paths that we chose form a shortest-path tree.
Now we want to know how long are the longest simple paths which contain K nodes in the shortest-path tree and how many these paths? Two simple paths are different if the sets of nodes they go through are different.
Input
The first line has a number T (T <= 10), indicating the number of test cases.
For each test case, the first line contains three integers n, m, k(1<=n<=30000,1<=m<=60000,2<=k<=n), denote the number of nodes, the number of edges and the nodes of required paths.
Then next m lines, each lines contains three integers a, b, c(1<=a, b<=n, 1<=c<=10000),denote there is an edge between a, b and length is c.
For each test case, the first line contains three integers n, m, k(1<=n<=30000,1<=m<=60000,2<=k<=n), denote the number of nodes, the number of edges and the nodes of required paths.
Then next m lines, each lines contains three integers a, b, c(1<=a, b<=n, 1<=c<=10000),denote there is an edge between a, b and length is c.
Output
For each case, output two numbers, denote the length of required paths and the numbers of required paths.
Sample Input
1 6 6 4 1 2 1 2 3 1 3 4 1 2 5 1 3 6 1 5 6 1
Sample Output
3 4
第一次写树的分治,感觉又涨姿势了,哈哈~~~
思路:先求最短路树,我的方法是先按节点号从小到大插入邻接表,求出最短路后用DFS按节点号从小到大跑出最短路树(感觉这种方法还是太挫了,想不到更好的),
然后用树的分治求解,总体思路就是每次找到树的重心,然后以重心为中心(也就是说这条路径要经过中心)进行K个节点最大树链的求解,重心怎么求呢,可以去百度,这里不做介绍了。用一个pow[k]和pnum[k]数组维护当前分治求解的这颗树中的经过k个节点最大的路径长度和数量,然后每次从中心出发开拓新的子树的时候计数即可。
#include
#include
#include
#include
#include
#include
#include
using namespace std; #define INF ((1<<31)-1) int n,m,k; int d; struct node{ int i; int d; node(int ii,int dd){i=ii;d=dd;} bool operator < (const node &a)const{return d>a.d;} }; struct vex{ int u; int v; int w; }; vex ve[60010]; priority_queue
q; vector
vv[30010]; int head[30010]; int edge[60010<<1]; int next[60010<<1]; int ww[60010<<1]; int dp[30010]; int vis[30010]; int head1[30010]; int edge1[60010]; int next1[60010]; int ww1[60010]; int num[30010]; int cnum[30010]; int center; int maxii; int total; int pow[30010]; int pnum[30010]; int val[30010]; int anss; int nanss; int maxi(int x,int y) { return x>y?x:y; } void add(int u,int v,int w) { edge[d]=v; ww[d]=w; next[d]=head[u]; head[u]=d++; } void add1(int u,int v,int w) { edge1[d]=v; ww1[d]=w; next1[d]=head1[u]; head1[u]=d++; } void djsk() { int i,temp; memset(vis,0,sizeof(vis)); for(i=1;i<=n;i++)dp[i]=INF; dp[1]=0; q.push(node(1,0)); while(!q.empty()) { node f=q.top(); q.pop(); if(vis[f.i])continue; vis[f.i]=1; for(i=head[f.i];i!=-1;i=next[i])if(dp[edge[i]]>(temp=dp[f.i]+ww[i])){ dp[edge[i]]=temp; q.push(node(edge[i],dp[edge[i]])); } } } void dfs(int u,int dis) { int i; vis[u]=1; for(i=head[u];i!=-1;i=next[i])if(!vis[edge[i]]&&dp[edge[i]]==dis+ww[i]){ // printf("%d %d\n",u,edge[i]); add1(u,edge[i],ww[i]); add1(edge[i],u,ww[i]); dfs(edge[i],dis+ww[i]); } } void dfs2(int u,int pre) { total++; num[u]=1; cnum[u]=0; int i; for(i=head1[u];i!=-1;i=next1[i])if(edge1[i]!=pre&&!val[edge1[i]]){ dfs2(edge1[i],u); num[u]+=num[edge1[i]]; if(cnum[u]
=k)return; vv[cent].push_back(dis); int temp; if((temp=dis+pow[k-1-cent])>anss) { // printf("%d %d %d haha\n",u,pow[k-1-cent],cent); anss=temp; nanss=pnum[k-1-cent]; } else if(temp==anss)nanss+=pnum[k-1-cent]; for(i=head1[u];i!=-1;i=next1[i])if(edge1[i]!=pre&&!val[edge1[i]])dfs4(edge1[i],u,dis+ww1[i],cent+1); } void diaozhatian(int u) { total=0; dfs2(u,0);//求出每个节点的子树节点数量和最大子树节点数量 if(total
pow[j]){ pow[j]=vv[j][kk]; // printf("\n%d %d\n\n",j,pow[j]); pnum[j]=1; } else if(vv[j][kk]==pow[j])pnum[j]++; } for(j=1;j<=k-1;j++)vv[j].clear(); } val[center]=1; for(i=head1[center];i!=-1;i=next1[i])if(!val[edge1[i]])diaozhatian(edge1[i]); } void solve() { djsk(); memset(pow,0,sizeof(pow)); memset(pnum,0,sizeof(pnum)); pnum[0]=1; memset(vis,0,sizeof(vis)); memset(val,0,sizeof(val)); d=0; dfs(1,0);//求出最短路树 // printf("\n"); diaozhatian(1);//树的分治 } bool cmp1(vex a,vex b) { if(a.u==b.u)return a.v>b.v; else return a.u
b.u; else return a.v