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

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

题面

BZOJ

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

题解

先考虑正常的\(dp\)
对于第一个,总和。设\(f[i]\)表示\(i\)的子树中的关键点的个数
转移:\(sum+=f[v]*(K-f[v])*len(i,v),f[i]+=f[v]\)
对于第二个和第三个,相当于维护树上最长链和最短链
这个就非常基础了,不写了。

现在再把这个\(dp\)放在虚树上做就行了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1001000
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Line{int v,next,w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int n,Q,K;
int fa[MAX],size[MAX],hson[MAX],dep[MAX],top[MAX],dfn[MAX],low[MAX],tim;
void dfs1(int u,int ff)
{
    fa[u]=ff;dep[u]=dep[ff]+1;size[u]=1;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(v==ff)continue;
        dfs1(v,u);size[u]+=size[v];
        if(size[v]>size[hson[u]])hson[u]=v;
    }
}
void dfs2(int u,int tp)
{
    top[u]=tp;dfn[u]=++tim;
    if(hson[u])dfs2(hson[u],tp);
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==hson[u]||v==fa[u])continue;
        dfs2(v,v);
    }
    low[u]=tim;
}
int LCA(int u,int v)
{
    while(top[u]^top[v])(dep[top[u]]<dep[top[v]])?v=fa[top[v]]:u=fa[top[u]];
    return dep[u]<dep[v]?u:v;
}
int S[MAX],p[MAX<<1];
bool vis[MAX];
bool cmp(int u,int v){return dfn[u]<dfn[v];}
int Min,Max,KK;
ll Sum;
int f1[MAX],f2[MAX],f3[MAX];
void DP(int u)
{
    if(vis[u])f1[u]=1,f2[u]=0,f3[u]=0;
    else f1[u]=0,f2[u]=1e9,f3[u]=-1e9;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;DP(v);
        Sum+=1ll*f1[v]*(KK-f1[v])*e[i].w;f1[u]+=f1[v];
        Min=min(Min,f2[u]+f2[v]+e[i].w);f2[u]=min(f2[u],f2[v]+e[i].w);
        Max=max(Max,f3[u]+f3[v]+e[i].w);f3[u]=max(f3[u],f3[v]+e[i].w);
    }
}
int main()
{
    n=read();
    for(int i=1;i<n;++i)
    {
        int u=read(),v=read();
        Add(u,v,0);Add(v,u,0);
    }
    dfs1(1,0);dfs2(1,1);
    memset(h,0,sizeof(h));
    Q=read();
    while(Q--)
    {
        K=KK=read();cnt=1;int tp=0;
        for(int i=1;i<=K;++i)vis[p[i]=read()]=true;
        sort(&p[1],&p[K+1],cmp);
        for(int i=K;i>1;--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(int i=1;i<=K;++i)
        {
            while(tp&&low[S[tp]]<dfn[p[i]])--tp;
            Add(S[tp],p[i],dep[p[i]]-dep[S[tp]]);
            S[++tp]=p[i];
        }
        Sum=0;Min=1e9;Max=-1e9;DP(p[1]);
        printf("%lld %d %d\n",Sum,Min,Max);
        for(int i=1;i<=K;++i)vis[p[i]]=false,h[p[i]]=0;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/9067086.html

时间: 2024-12-10 19:25:09

【BZOJ3611】大工程(虚树,动态规划)的相关文章

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

LibreOJ #2219. 「HEOI2014」大工程

二次联通门 : LibreOJ #2219. 「HEOI2014」大工程 /* LibreOJ #2219. 「HEOI2014」大工程 虚树 + dp 对于每次的关键点建好虚树后 考虑树形dp dp[i] 表示在以i为根的子树路径总长 size[i] 表示在以i为根的子树中关键点的个数 maxn为以i为根的子树中到关键点到根最长的路径长 ans1自己推一推就好 对于ans2 如果i是关键点,则直接用maxn[i]更新答案 否则用maxn[i]+maxn[child[i]]+dis(i,son[

[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]大工程)

题面 传送门 虚树 把跟询问有关的点拿出来建树,为了方便树\(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)

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

【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为根

[bzoj3611][Heoi2014]大工程

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

数据结构(虚树,动态规划):HNOI 2014 世界树

Hnoi2014 世界树 Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. 世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相 同.有的聚居地之间有双向的道路相连,道路的长度为1.保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互