1. 程式人生 > >BZOJ3611 [Heoi2014]大工程 【虛樹】

BZOJ3611 [Heoi2014]大工程 【虛樹】

計劃 char 輸出格式 tag -- define flag int 多個

題目

國家有一個大工程,要給一個非常大的交通網絡裏建一些新的通道。
我們這個國家位置非常特殊,可以看成是一個單位邊權的樹,城市位於頂點上。
在 2 個國家 a,b 之間建一條新通道需要的代價為樹上 a,b 的最短路徑。
現在國家有很多個計劃,每個計劃都是這樣,我們選中了 k 個點,然後在它們兩兩之間 新建 C(k,2)條 新通道。
現在對於每個計劃,我們想知道:
1.這些新通道的代價和
2.這些新通道中代價最小的是多少
3.這些新通道中代價最大的是多少

輸入格式

第一行 n 表示點數。

接下來 n-1 行,每行兩個數 a,b 表示 a 和 b 之間有一條邊。
點從 1 開始標號。 接下來一行 q 表示計劃數。
對每個計劃有 2 行,第一行 k 表示這個計劃選中了幾個點。
第二行用空格隔開的 k 個互不相同的數表示選了哪 k 個點

輸出格式

輸出 q 行,每行三個數分別表示代價和,最小代價,最大代價。

輸入樣例

10

2 1

3 2

4 1

5 2

6 4

7 5

8 6

9 7

10 9

5

2

5 4

2

10 4

2

5 2

2

6 1

2

6 1

輸出樣例

3 3 3

6 6 6

1 1 1

2 2 2

2 2 2

提示

n<=1000000

q<=50000並且保證所有k之和<=2*n

題解

好久沒寫虛樹了,一寫整個人就虛完了,

這題沒什麽難點,建完虛樹後比較基礎的樹形dp就完了

最大值就維護當前子樹最大值,然後嘗試將一個子樹最大值與當前子樹中最大值相加更新答案
最小值類似
總和呢,考慮一個子樹內的所有點要往上走,都要經過這條邊,那麽有\(siz[son] * (k - siz[son])\)

種組合,乘以邊長作為貢獻

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std; const int maxn = 1000005,maxm = 2000005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int hh[maxn],nn = 2; int h[maxn],ne = 2,de[maxn]; struct EDGE{int to,nxt,w;}e[maxm],ed[maxm]; inline void add(int u,int v){ e[nn] = (EDGE){v,hh[u],0}; hh[u] = nn++; e[nn] = (EDGE){u,hh[v],0}; hh[v] = nn++; } inline void build(int u,int v,int w){ ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++; ed[ne] = (EDGE){u,h[v],w}; h[v] = ne++; de[u]++; de[v]++; } int n,K,a[maxn],fa[maxn][21],dep[maxn],dfn[maxn],cnt; void dfs(int u){ dfn[u] = ++cnt; for (int i = 1; i <= 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1]; for (int k = hh[u],to; k; k = e[k].nxt) if ((to = e[k].to) != fa[u][0]){ fa[to][0] = u; dep[to] = dep[u] + 1; dfs(to); } } int lca(int u,int v){ if (dep[u] < dep[v]) swap(u,v); for (int i = 0,d = dep[u] - dep[v]; (1 << i) <= d; i++) if ((1 << i) & d) u = fa[u][i]; if (u == v) return u; for (int i = 20; i >= 0; i--) if (fa[u][i] != fa[v][i]){ u = fa[u][i]; v = fa[v][i]; } return fa[u][0]; } int st[maxn],top; inline bool cmp(const int& a,const int& b){ return dfn[a] < dfn[b]; } void rebuild(){ top = 0; ne = 2; sort(a + 1,a + 1 + K,cmp); st[++top] = 1; for (int i = 1; i <= K; i++){ int u = a[i],v = lca(u,st[top]); if (v == st[top]) st[++top] = u; else { while (true){ if (dep[v] >= dep[st[top - 1]]){ build(v,st[top],dep[st[top]] - dep[v]); top--; st[++top] = v; break; } build(st[top],st[top - 1],dep[st[top]] - dep[st[top - 1]]); top--; } st[++top] = u; } } while (top > 1) build(st[top],st[top - 1],dep[st[top]] - dep[st[top - 1]]),top--; } int tag[maxn]; LL sum,gmax,gmin,mn[maxn],mx[maxn],siz[maxn]; void DFS(int u){ mx[u] = tag[u] ? 0 : -INF; mn[u] = tag[u] ? 0 : INF; siz[u] = tag[u]; Redge(u) if (dep[to = ed[k].to] > dep[u]){ DFS(to); sum += siz[to] * (K - siz[to]) * ed[k].w; siz[u] += siz[to]; gmin = min(gmin,mn[u] + mn[to] + ed[k].w); gmax = max(gmax,mx[u] + mx[to] + ed[k].w); mn[u] = min(mn[u],mn[to] + ed[k].w); mx[u] = max(mx[u],mx[to] + ed[k].w); } h[u] = de[u] = 0; } void solve(){ rebuild(); for (int i = 1; i <= K; i++) tag[a[i]] = 1; sum = gmax = 0; gmin = INF; if (de[1] == 1 && !tag[1]){ DFS(ed[h[1]].to); h[1] = de[1] = 0; } else DFS(1); printf("%lld %lld %lld\n",sum,gmin,gmax); for (int i = 1; i <= K; i++) tag[a[i]] = 0; } int main(){ n = read(); for (int i = 1; i < n; i++) add(read(),read()); dfs(1); int q = read(); while (q--){ K = read(); for (int i = 1; i <= K; i++) a[i] = read(); solve(); } return 0; }

BZOJ3611 [Heoi2014]大工程 【虛樹】