虚树(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))
using namespace std;
typedef long long ll;

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

const int maxn(1e6 + 5);
const int inf(1e9);

int n, first1[maxn], cnt, first2[maxn], id[maxn], p[maxn];
ll g[maxn], f[maxn], num[maxn], sum, mx, mn, tot;
int dfn[maxn], size[maxn], son[maxn], fa[maxn];
int s[maxn], top[maxn], idx, deep[maxn];

struct Edge{
    int to, next;
} e2[maxn << 1], e1[maxn << 1];

IL void Add1(RG int u, RG int v){
    e1[cnt] = (Edge){v, first1[u]}, first1[u] = cnt++;
}

IL void Add2(RG int u, RG int v){
    e2[cnt] = (Edge){v, first2[u]}, first2[u] = cnt++;
}

IL void Dfs1(RG int u){
    size[u] = 1;
    for(RG int e = first1[u]; e != -1; e = e1[e].next){
        RG int v = e1[e].to;
        if(!size[v]){
            deep[v] = deep[u] + 1;
            Dfs1(v), size[u] += size[v], fa[v] = u;
            if(size[v] > size[son[u]]) son[u] = v;
        }
    }
}

IL void Dfs2(RG int u, RG int tp){
    top[u] = tp, dfn[u] = ++idx;
    if(son[u]) Dfs2(son[u], tp);
    for(RG int e = first1[u]; e != -1; e = e1[e].next)
        if(!dfn[e1[e].to]) Dfs2(e1[e].to, e1[e].to);
}

IL int LCA(RG int u, RG int v){
    while(top[u] ^ top[v])
        deep[top[u]] > deep[top[v]] ? u = fa[top[u]] : v = fa[top[v]];
    return deep[u] > deep[v] ? v : u;
}

IL int Dis(RG int u, RG int v){
    RG int lca = LCA(u, v);
    return deep[u] + deep[v] - 2 * deep[lca];
}

IL int Cmp(RG int u, RG int v){
    return dfn[u] < dfn[v];
}

IL void DP(RG int u){
    g[u] = inf, f[u] = -inf;
    if(num[u]) g[u] = f[u] = 0;
    for(RG int e = first2[u]; e != -1; e = e2[e].next){
        RG int v = e2[e].to, w = Dis(u, v);
        DP(v), num[u] += num[v];
        sum += (tot - num[v]) * num[v] * w;
        mn = min(mn, g[u] + w + g[v]);
        mx = max(mx, f[u] + w + f[v]);
        g[u] = min(g[u], g[v] + w);
        f[u] = max(f[u], f[v] + w);
    }
}

int main(){
    n = Input();
    for(RG int i = 1; i <= n; ++i) first1[i] = first2[i] = -1;
    for(RG int i = 1; i < n; ++i){
        RG int u = Input(), v = Input();
        Add1(u, v), Add1(v, u);
    }
    Dfs1(1), Dfs2(1, 1);
    for(RG int q = Input(); q; --q){
        RG int k = Input(); cnt = 0, tot = k;
        for(RG int i = 1; i <= k; ++i) p[i] = Input(), num[p[i]] = 1;
        sort(p + 1, p + k + 1, Cmp);
        for(RG int i = 1, t = k; i < t; ++i) p[++k] = LCA(p[i], p[i + 1]);
        sort(p + 1, p + k + 1, Cmp), k = unique(p + 1, p + k + 1) - p - 1;
        for(RG int i = 1; i <= k; ++i) first2[p[i]] = -1;
        RG int t = 0;
        for(RG int i = 1; i <= k; ++i){
            while(t && dfn[p[i]] >= dfn[s[t]] + size[s[t]]) --t;
            if(t) Add2(s[t], p[i]);
            s[++t] = p[i];
        }
        mx = sum = 0, mn = inf;
        DP(p[1]);
        printf("%lld %lld %lld\n", sum, mn, mx);
        for(RG int i = 1; i <= k; ++i) num[p[i]] = 0;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cjoieryl/p/9113969.html

时间: 2024-10-09 06:35:07

虚树(Bzoj3611: [Heoi2014]大工程)的相关文章

[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]大工程 【虚树】

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

[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\)的贡献 第二个操作是最大的距离,这不就是直径吗,子树内部最长链和次长链拼一下就好了 第三个操作是所有点对之间的最小值,因为虚树上有一些点并不是被询问的点,所以不能直接从虚树上选择最

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