HDU - 4871 Shortest-path tree (最短路徑樹+ 樹分治)
阿新 • • 發佈:2018-10-04
get -- epp return 深度 分析 std inf +=
題意:給你一張帶權無向圖,先求出這張圖從點1出發的最短路樹,再求在樹上經過k個節點最長的路徑值,以及個數.
分析:首先求最短路樹,跑一遍最短路之後dfs一遍即可建出最短路樹.
第二個問題,樹分治解決.
對於以root為根的樹,所求的路徑只會有兩種情況.
1) 存在於root的子樹中,不經過root;
2) 經過root,路徑的兩端在root的兩棵子樹中.
第一種情況,我們交給分治去解決,
第二種情況,需要知道所有子樹中走過j步能到達的最遠距離,以及其方案數.通過dfs可以得到這些信息.用這些信息,再去和其他子樹的信息結合,去更新答案.
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int INF = 1<<30; const int MAXN = 1e5+5; struct Edge{ int v,w,next; }E[MAXN<<2]; int head[MAXN],tot , son[MAXN], Max[MAXN], siz[MAXN], dep[MAXN] ,now[MAXN]; LL cnt[MAXN], Maxcnt[MAXN] , ansnum; int maxdep , clk, ansdep, minson; bool vis[MAXN]; int root,N,M,k; void init() { memset(vis,0,sizeof(vis)); memset(head,-1,sizeof(head)); tot = ansnum = clk = 0; ansdep = 0; } void Add(int u,int v,int w){ E[tot] =(Edge){v,w,head[u]}; head[u] = tot++; } //求子樹的重心 void getsize(int u, int fa) { siz[u] = 1; for (int i = head[u]; i != -1; i = E[i].next) { int v = E[i].v; if (v == fa || vis[v]) continue; getsize(v, u); siz[u] += siz[v]; } } void getroot(int u, int fa, int s) { int max1 = 0; for (int i = head[u]; i != -1; i = E[i].next) { int v = E[i].v; if (v == fa || vis[v]) continue; getroot(v, u, s); max1 = max(max1, siz[v]); } max1 = max(max1, s - siz[u]); if (minson > max1) { minson = max1; root = u; } } void getMaxdep(int depp, int u, int fa) { maxdep = max(maxdep, depp); for (int i = head[u]; i != -1; i = E[i].next) { int v = E[i].v; if (v == fa || vis[v]) continue; getMaxdep(depp + 1, v, u); } } void getdep(int depp, int len, int u, int fa) { if (dep[depp] < len) { dep[depp] = len; cnt[depp] = 1; } else if (dep[depp] == len) cnt[depp]++; if (depp >= k) return; for (int i = head[u]; i != -1; i = E[i].next) { int v = E[i].v; if (v == fa || vis[v]) continue; getdep(depp + 1, len + E[i].w, v, u); } } void getans(int u) { vis[u] = 1; for (int i = head[u]; i != -1; i = E[i].next){ int v = E[i].v; if (vis[v]) continue; minson = INF; getsize(v, -1); getroot(v, -1, siz[v]); getans(root); } /* 求經過k個節點的最長路徑,以及其方案數 答案可能有兩種情況, 一是存在於u的子樹中,這種情況交給分治處理 二是該路徑經過了重心本身,在以下代碼中處理 */ clk++; now[0] = clk; dep[0] = 0 ,cnt[0] = 1; Maxcnt[0] = 1, Max[0] = 0; for (int i = head[u]; i != -1; i = E[i].next){ int v = E[i].v; if (vis[v]) continue; maxdep = -1; //獲取這棵子樹的最大深度 getMaxdep(1, v, u); for (int j = 0; j <= maxdep && j <= k; j++) dep[j] = -1; //獲取這棵子樹中經過i個節點,所能走的最長距離以及方案數, //分別記錄在dep[i], 和cnt[i]中 getdep(1, E[i].w, v, u); //根據當前信息和這棵子樹的信息更新答案 for (int j = 0; j <= maxdep && j < k; j++){ int tmp = k - j - 1; if (now[tmp] != clk) continue; if (ansdep < Max[tmp] + dep[j]){ ansdep = Max[tmp] + dep[j]; ansnum = Maxcnt[tmp] * cnt[j]; } else if (ansdep == Max[tmp] + dep[j]){ ansnum += Maxcnt[tmp] * cnt[j]; } } //用這個子樹的信息更新當前的信息 //Max[i]記錄之前已經訪問過的子樹中,經過i個節點能走過的最長距離 //Maxcnt[i] 記錄其方案數 for (int j = 1; j <= maxdep && j < k; j++){ if (now[j] != clk || Max[j] < dep[j]){ Max[j] = dep[j]; Maxcnt[j] = cnt[j]; now[j] = clk; } else if (now[j] == clk && Max[j] == dep[j]){ Maxcnt[j] += cnt[j]; } } } vis[u] = 0; } ///////////最短路樹 struct Dij{ struct Edge{ int v, w; bool operator < (const Edge & rhs) const{ return v<rhs.v; } }; vector<Edge> G[MAXN]; int N,d[MAXN]; bool vis[MAXN]; struct HeapNode{ int u,val; bool operator < (const HeapNode & rhs) const{ return val>rhs.val; } }; void init(int N){ this -> N = N; memset(vis,0,sizeof(vis)); for(int i=0;i<=N;++i) G[i].clear(); } void AddEdge(int u,int v,int w){ G[u].push_back((Edge){v,w}); } void dijkstra(int s){ for(int i=1;i<=N;++i){ sort(G[i].begin(),G[i].end()); } for(int i=0;i<=N;++i) d[i] = INF; d[s] = 0; priority_queue< HeapNode > Q; Q.push((HeapNode){s,0}); while(!Q.empty()){ HeapNode x = Q.top(); Q.pop(); if(vis[x.u]) continue; vis[x.u] = 1; int u = x.u, sz = G[u].size(); for(auto & e : G[u]){ int v = e.v; if(d[v]> d[u]+e.w){ d[v] = d[u] + e.w; Q.push((HeapNode){v,d[v]}); } } } } void dfs(int u,int fa){ vis[u] = true; for(auto & e: G[u]){ int v= e.v; if(v==fa || vis[v]) continue; if(d[v]== d[u]+e.w){ Add(u,v,e.w); Add(v,u,e.w); dfs(v,u); } } } }G; ////////////////// main int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int T; scanf("%d",&T); while(T--){ scanf("%d %d %d",&N, &M, &k); G.init(N); int u,v,w; while(M--){ scanf("%d %d %d",&u, &v, &w); G.AddEdge(u,v,w); G.AddEdge(v,u,w); } G.dijkstra(1); init(); //樹的初始化 memset(G.vis,0,sizeof(G.vis)); G.dfs(1,-1); //建最短路徑樹 memset(now,-1,sizeof(now)); root = -1; minson = INF; getroot(1,-1,N); getans(root); printf("%d %lld\n",ansdep,ansnum); } return 0; }
HDU - 4871 Shortest-path tree (最短路徑樹+ 樹分治)