bzoj3611大工程 虚树+树型动规

来源:互联网 发布:java写入文本文件 编辑:程序博客网 时间:2024/05/07 12:17

3611: [Heoi2014]大工程

Time Limit: 60 Sec  Memory Limit: 512 MB
Submit: 1616  Solved: 688
[Submit][Status][Discuss]

Description

国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。 
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。 
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
 现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
现在对于每个计划,我们想知道:
 1.这些新通道的代价和
 2.这些新通道中代价最小的是多少 
3.这些新通道中代价最大的是多少

Input

第一行 n 表示点数。

 接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。
点从 1 开始标号。 接下来一行 q 表示计划数。
对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
 第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。

Output

输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。 

Sample Input

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

Sample Output

3 3 3
6 6 6
1 1 1
2 2 2
2 2 2

HINT

n<=1000000 


q<=50000并且保证所有k之和<=2*n 

Source

鸣谢佚名上传


题目大意:给一棵树,边权都是1,求任意k个点两两距离和、最小最大值
看到Σk<=n << 1就知道一定是虚树的题目,然而树型动规却想不出来,TT
膜拜各种题解之后总算是明白怎么个动规法。
先把虚树建出来
对于距离和,设f[i]为子树各个工程到根的距离和。那么f[u] = sum(f[v] + size[v] * w[v])其中v为u的儿子,size表示工程数目,w表示儿子父边权值(新建虚树上的边权,做的时候用deep减一下可以得出)统计的时候,每统计一颗子树就更新总距离,更新方式是tot += (f[u] + size[u] * e2[i].w) * size[v] + f[v] * size[u],意思就是u之前所有子树的工程到v的距离和加上v子树的所有工程到v的距离。好像点分治有木有
然后就是最大最小值。一样地,mn[i],mx[i]表示从所有子树到根i的最小最大值。那么显然,如果i本身是一个工程,那么最小最大值初值都可以为0,否则i必须从其他的工程走到自己才能计入答案。更新的时候就是mn/mx[i] = min/max(mn[i]/mx[i], mn[v] + w[v]),就是从别的子树走到自己, 更新总答案的时候mnans/mxans = min/max(mnans/mxans, mn[u]/mx[u] + mn[v]/mx[v] + w[v])也就是之前子树到根的答案与现在子树到根的答案。
总结一下这道题,其实建虚树很显然很暴力,关键还是树dp,而树dp的做法真的超级像点分治,个人觉得这题点分治也能做,树dp写出来也很像是点分治的做法。
最后注意要区别这个点在虚树中是否为选中的点。

#include<iostream>#include<cstdlib>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<cmath>#include<set>#define maxn 1000005#define inf 2000000000using namespace std;int read() {char ch = getchar(); int x = 0, f = 1;while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}return x * f;}int pre[maxn][2], top, deep[maxn];struct edge {int to, next, w;void add(int u, int v, bool p){if(u == v) return;to = v; next = pre[u][p];pre[u][p] = top; if(p) w = deep[v] - deep[u];}}e[maxn * 2], e2[maxn * 2];void adds(int v, int u) {e[++top].add(u, v, 0);e[++top].add(v, u, 0);} int fa[maxn], son[maxn], dson[maxn], d[maxn], in[maxn];int cnt, n, m, Q, h[maxn], st[maxn];long long mxans, mnans, tot, f[maxn], mn[maxn], mx[maxn], size[maxn];bool vis[maxn];bool cmp(int x, int y) {return in[x] < in[y];}void dfs1(int u, int pa) {fa[u] = pa; deep[u] = deep[pa] + 1; son[u] = 1; dson[u] = 0;for(int i = pre[u][0]; i; i = e[i].next) {if(e[i].to == pa) continue;int v = e[i].to;dfs1(v, u);son[u] += son[v];if(son[v] > son[dson[u]]) dson[u] = v;}}void dfs2(int u, int chain) {in[u] = ++tot; d[u] = chain;if(!dson[u]) return;dfs2(dson[u], chain);for(int i = pre[u][0]; i; i = e[i].next) if(e[i].to != dson[u] && e[i].to != fa[u])dfs2(e[i].to, e[i].to);}int lca(int u, int v) {while(d[u] != d[v]) {if(deep[d[u]] < deep[d[v]]) swap(u, v);u = fa[d[u]];}if(in[u] > in[v]) swap(u, v); return u;}void init() {n = read();for(int i = 1;i < n; ++i) adds(read(), read());dfs1(1, 0); dfs2(1, 1);}void Itree_build() {m = read();for(int i = 1;i <= m; ++i) vis[h[i] = read()] = 1;sort(h + 1, h + m + 1, cmp); top = 0;st[++cnt] = 1;for(int i = 1;i <= m; ++i) {int cur = h[i], pa = lca(cur, st[cnt]);if(pa == st[cnt]) st[++cnt] = cur;else {while(pa == lca(cur, st[cnt - 1])) {e2[++top].add(st[cnt - 1], st[cnt], 1);pa = lca(cur, st[--cnt]);}e2[++top].add(pa, st[cnt], 1);st[cnt] = pa; st[++cnt] = cur;}}while(--cnt) e2[++top].add(st[cnt], st[cnt + 1], 1);}void dp(int u) {size[u] = vis[u]; f[u] = 0;mn[u] = vis[u] ? 0 : inf;mx[u] = vis[u] ? 0 : -inf;for(int i = pre[u][1]; i; i = e2[i].next) {int v = e2[i].to;dp(v);tot += (f[u] + size[u] * e2[i].w) * size[v] + f[v] * size[u];f[u] += f[v] + size[v] * e2[i].w;size[u] += size[v];mnans = min(mnans, mn[u] + mn[v] + e2[i].w);mn[u] = min(mn[u], mn[v] + e2[i].w);mxans = max(mxans, mx[u] + mx[v] + e2[i].w);mx[u] = max(mx[u], mx[v] + e2[i].w);}pre[u][1] = 0;}void solve() {Itree_build();tot = 0; mnans = inf; mxans = -inf;dp(1);printf("%lld %lld %lld\n", tot, mnans, mxans);for(int i = 1;i <= m; ++i) vis[h[i]] = 0;}int main(){init();Q = read();for(int i = 1;i <= Q; ++i) solve();return 0;}


原创粉丝点击