bzoj3743 [Coci2015]Kamp 常州模擬賽d6t2
阿新 • • 發佈:2017-08-26
一個數 ner ret tput pac hint 都是 bsp 個數
Submit: 484 Solved: 229
[Submit][Status][Discuss]
1 2 4
1 3 1
2 5 1
2 4 2
4 7 3
4 6 2
3
7
15
10
13
16
15
10
K <= N <= 500000
1 <= x,y <= N, 1 <= z <= 1000000 分析:本來想用狀壓dp的,但是n這麽大,怎麽壓的下來。其實這道題有一個結論:如果我們把要送達的點建立一棵樹,那麽答案就是這棵樹上的邊權和*2+i到這棵樹的最短距離(假設為j點)-j到樹上最遠一點的距離.其實也很好想為什麽,要遍歷樹上的點最少肯定要每條邊都走兩次,i點要到這棵樹上肯定要走最短距離,我們到一個點後就不回起點了,那麽肯定終點在最遠的那個點上.
那麽想要做出這道題就要用到4個dfs.首先求出每個點在不在樹上,然後求出i到樹上最近的點和最短距離,接下來求出每個點到它的葉子節點的最長距離和次長距離,最後求出每個點的最長距離.
第一步和第二步很好理解,關鍵是最後兩步,我們求最長距離為什麽還要求每個點到它的葉子節點的最長距離和次長距離呢?其實這有點像樹鏈剖分,我們把樹鏈分為最長鏈和次長鏈,位於最長鏈的兒子成為重兒子,重兒子的最遠距離點肯定在它的子節點中或者次長鏈中,這就包含了最長鏈和次長鏈兩種情況,沒有其它情況了,輕兒子的最遠距離也肯定在它的子節點或者最長鏈中,所以我們先求出父節點到子節點的最遠距離和次遠距離,然後利用父節點更新子節點,很難想到啊.
3743: [Coci2015]Kamp
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 484 Solved: 229
[Submit][Status][Discuss]
Description
一顆樹n個點,n-1條邊,經過每條邊都要花費一定的時間,任意兩個點都是聯通的。 有K個人(分布在K個不同的點)要集中到一個點舉行聚會。 聚會結束後需要一輛車從舉行聚會的這點出發,把這K個人分別送回去。 請你回答,對於i=1~n,如果在第i個點舉行聚會,司機最少需要多少時間把K個人都送回家。
Input
第一行兩個數,n,K。 接下來n-1行,每行三個數,x,y,z表示x到y之間有一條需要花費z時間的邊。 接下來K行,每行一個數,表示K個人的分布。
Output
輸出n個數,第i行的數表示:如果在第i個點舉行聚會,司機需要的最少時間。
Sample Input
7 21 2 4
1 3 1
2 5 1
2 4 2
4 7 3
4 6 2
3
7
Sample Output
1115
10
13
16
15
10
HINT
【數據規模】
K <= N <= 500000
1 <= x,y <= N, 1 <= z <= 1000000 分析:本來想用狀壓dp的,但是n這麽大,怎麽壓的下來。其實這道題有一個結論:如果我們把要送達的點建立一棵樹,那麽答案就是這棵樹上的邊權和*2+i到這棵樹的最短距離(假設為j點)-j到樹上最遠一點的距離.其實也很好想為什麽,要遍歷樹上的點最少肯定要每條邊都走兩次,i點要到這棵樹上肯定要走最短距離,我們到一個點後就不回起點了,那麽肯定終點在最遠的那個點上.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 500010; int n, k,head[maxn],to[maxn * 2],nextt[maxn * 2],w[maxn * 2],tot = 1,ner[maxn],son[maxn]; long long mind[maxn],maxd[maxn],secd[maxn],d[maxn],sum; bool flag[maxn],vis[maxn]; void add(int x, int y, int z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } bool dfs1(int x) //找樹 { vis[x] = 1; for (int i = head[x]; i; i = nextt[i]) { int v = to[i]; if (!vis[v]) { if (dfs1(v)) { flag[x] = 1; sum += w[i]; } } } return flag[x]; } void dfs2(int x) //找最短距離 { vis[x] = 1; if (flag[x]) ner[x] = x; for (int i = head[x]; i; i = nextt[i]) { int v = to[i]; if (!vis[v] && !flag[v]) { mind[v] = mind[x] + w[i]; ner[v] = ner[x]; } if (!vis[v]) dfs2(v); } } long long dfs3(int x) //最長距離+次長距離 { vis[x] = 1; for (int i = head[x]; i; i = nextt[i]) { int v = to[i]; if (!vis[v] && flag[v]) { long long t = dfs3(v) + w[i]; if (t > maxd[x]) { secd[x] = maxd[x]; maxd[x] = t; son[x] = v; } else if (t > secd[x]) secd[x] = t; } } return maxd[x]; } void dfs4(int x, long long lmax) //最後的更新 { vis[x] = 1; for (int i = head[x]; i; i = nextt[i]) { int v = to[i]; d[x] = max(lmax, maxd[x]); if (flag[v] && !vis[v]) { if (v == son[x]) dfs4(v, max(lmax, secd[x]) + w[i]); else dfs4(v, max(lmax, maxd[x]) + w[i]); } } } int main() { memset(flag, false, sizeof(flag)); scanf("%d%d", &n, &k); for (int i = 1; i < n; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); add(x, y, z); add(y, x, z); } int t; for (int i = 1; i <= k; i++) { scanf("%d", &t); flag[t] = 1; } dfs1(t); memset(vis, 0, sizeof(vis)); dfs2(t); memset(vis, 0, sizeof(vis)); dfs3(t); memset(vis, 0, sizeof(vis)); dfs4(t, 0); for (int i = 1; i <= n; i++) printf("%lld\n", sum * 2 + mind[i] - d[ner[i]]); return 0; }
bzoj3743 [Coci2015]Kamp 常州模擬賽d6t2