BZOJ3611 [Heoi2014]大工程 【虚树】

题目

国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。

我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。

在 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;
}

原文地址:https://www.cnblogs.com/Mychael/p/8666750.html

时间: 2024-10-01 03:27:47

BZOJ3611 [Heoi2014]大工程 【虚树】的相关文章

BZOJ 3611: [Heoi2014]大工程 [虚树 DP]

传送门 题意: 多次询问,求最长链最短链链总长 煞笔$DP$记录$d,c,f,g$ $MD$该死拍了一下午没问题然后交上去就$T$ 然后发现树链剖分写成$size[v]+=size[u]$ 我想知道我随机生成的大数据是怎么跑过去的!!!!!!!! #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using

[Bzoj3611][Heoi2014]大工程(虚树)

3611: [Heoi2014]大工程 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 2000  Solved: 837[Submit][Status][Discuss] Description 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径. 现在国家有很多个计划,每个计划都是这样,

虚树(Bzoj3611: [Heoi2014]大工程)

题面 传送门 虚树 把跟询问有关的点拿出来建树,为了方便树\(DP\) 在\(LCA\)处要合并答案,那么把这些点的\(LCA\)也拿出来 做法:把点按\(dfs\)序排列,然后求出相邻两个点的\(LCA\),把这些点建一个虚树,维护一个栈就好了 Sol 虚树+树\(DP\) # include <bits/stdc++.h> # define IL inline # define RG register # define Fill(a, b) memset(a, b, sizeof(a))

[bzoj3611][Heoi2014]大工程

看题目感觉应该就是传说中的虚树? 然后跑去学了一发...自己YY了一下然后挂飞..于是就只好抄模板了T_T 建完虚树就是个树形dp... 对于询问总和:每条边对答案的贡献是边权*一端的节点数*另一端的节点数.(这里的节点不包括建虚树时添上去的点) 对于询问最小值最大值,每次计算出经过这个节点的最长||最短路径长度就好了.. 大概这种题条件都有一个sigma(K)<=n之类的..而且题目求的东西得符合区间加法...不然你把边合在一起也没用>_< 链剖求lca果然快...速度能进前10...

【BZOJ3611】大工程(虚树,动态规划)

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

bzoj 3611: [Heoi2014]大工程

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

[HEOI2014]大工程

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

【bzoj3611】 大工程

http://www.lydsy.com/JudgeOnline/problem.php?id=3611 (题目链接) 搞了1天= =,感觉人都变蠢了... 题意:给出一个n个节点的树,每条边边权为1,给出q个询问,每次询问K个关键点,求出这k个点之间的两两距离和.最小距离和最大距离. solution  构造虚树,见 http://blog.csdn.net/MashiroSky/article/details/51971718  之后在虚树上dp,有点麻烦.  用size[u]表示在以u为根

【[HEOI2014]大工程 】

可能是虚树板子题了 首先先把虚树建出来,但是这里和那道虚树的入门题不一样,这里所有的询问点都得在虚树里,所以不会存在那种直接不如栈的点 之后我们考虑一下这个三个要求的东西 第一个操作我们需要统计虚树上每一条边的贡献,即被多少个点对经过,根据乘法原理显然有\((t-sz[x])\times sz[x]\times w\)的贡献 第二个操作是最大的距离,这不就是直径吗,子树内部最长链和次长链拼一下就好了 第三个操作是所有点对之间的最小值,因为虚树上有一些点并不是被询问的点,所以不能直接从虚树上选择最