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 行,每行三个数分别表示代价和,最小代价,最大代价。

题解:建出来虚树后就不是很难了

#include<bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin), freopen(s".out","w",stdout)
#define maxn 2000001
#define inf 1000000000
#define ll long long
using namespace std;
vector<int>G[maxn];
int edges,tim,root,top;
int hd[maxn], to[maxn<<1], val[maxn<<1], nex[maxn<<1];
int dep[maxn],Top[maxn],hson[maxn],siz[maxn],dfn[maxn],fa[maxn],arr[maxn],S[maxn],mk[maxn];
inline void add(int u,int v)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=1;
}
void dfs1(int u,int ff)
{
fa[u]=ff,siz[u]=1,dep[u]=dep[ff]+1,dfn[u]=++tim;
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==ff) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[hson[u]]) hson[u]=v;
}
}
void dfs2(int u,int tp)
{
Top[u]=tp;
if(hson[u]) dfs2(hson[u],tp);
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==fa[u]||v==hson[u]) continue;
dfs2(v,v);
}
}
inline int LCA(int x,int y)
{
while(Top[x]!=Top[y])
{
dep[Top[x]] > dep[Top[y]] ? x = fa[Top[x]] : y = fa[Top[y]];
}
return dep[x] < dep[y] ? x : y;
}
inline int getdis(int x,int y)
{
return dep[x] + dep[y] - (dep[LCA(x,y)] << 1);
}
inline void addvir(int u,int v)
{
G[u].push_back(v);
}
void insert(int x)
{
if(top<=1) { S[++top]=x; return; }
int lca=LCA(x, S[top]);
if(lca == S[top]) { S[++top] = x; return; }
while(top > 1 && dep[S[top - 1]] >= dep[lca]) addvir(S[top - 1], S[top]), --top;
if(lca != S[top]) addvir(lca, S[top]), S[top] = lca;
S[++top] = x;
}
bool cmp(int i,int j)
{
return dfn[i] < dfn[j];
}
ll ans=0,a1,a2;
int size[maxn],d1[maxn],d2[maxn],dmin1[maxn],k,dmin2[maxn];
void DP(int x)
{
size[x]=mk[x];
d1[x]=d2[x]=0;
if(!mk[x]) d1[x]=d2[x]=-inf;
dmin1[x]=dmin2[x]=inf;
if(mk[x]) dmin1[x]=0;
for(int i=0;i<G[x].size();++i)
{
int v = G[x][i],w = dep[G[x][i]] - dep[x];
DP(v);
if(mk[v])
{
if(w <= dmin1[x]) dmin2[x]=dmin1[x], dmin1[x]=w;
else if(w < dmin2[x]) dmin2[x]=w;
}
else
{
if(w + dmin1[v] <= dmin1[x]) dmin2[x]=dmin1[x], dmin1[x]=w + dmin1[v];
else if(w + dmin1[v] < dmin2[x]) dmin2[x] = w + dmin1[v];
}
int curd=w+d1[v];
if(curd >= d1[x])
{
d2[x]=d1[x], d1[x]=curd;
}
else if(curd > d2[x])
{
d2[x] = curd;
}
ans+=1ll*size[v]*w*(k-size[v]),size[x]+=size[v];
}
a1=max(a1, 1ll*(d1[x] + d2[x]));
a2=min(a2, 1ll*(dmin1[x] + dmin2[x]));
}
void init(int x)
{
d1[x]=d2[x]=0;
dmin1[x]=dmin2[x]=inf;
for(int i=0;i<G[x].size();++i) init(G[x][i]);
G[x].clear();
}
inline void work()
{
scanf("%d",&k);
for(int i=1;i<=k;++i) scanf("%d",&arr[i]);
for(int i=1;i<=k;++i) mk[arr[i]] = 1;
sort(arr+1,arr+1+k,cmp);
top=S[0]=root=ans=0;
if(arr[1]!=1) S[top=1]=1;
for(int i=1;i<=k;++i) insert(arr[i]);
while(top > 1) addvir(S[top-1], S[top]),--top;
a1=-inf, a2=inf, DP(1);
printf("%lld %lld %lld\n",ans,a2,a1);
init(1);
for(int i=1;i<=k;++i) mk[arr[i]]=0;
}
int main()
{
// setIO("input");
int n;
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b), add(b,a);
}
dfs1(1,0),dfs2(1,1);
int Q;
scanf("%d",&Q);
for(int i=1;i<=Q;++i) work();
return 0;
}

  

luogu P4103 [HEOI2014]大工程 虚树 + 树形 DP的更多相关文章

  1. 洛谷P4103 [HEOI2014]大工程(虚树 树形dp)

    题意 链接 Sol 虚树. 首先建出虚树,然后直接树形dp就行了. 最大最小值直接维护子树内到该节点的最大值,然后合并两棵子树的时候更新一下答案. 任意两点的路径和可以考虑每条边两边的贡献,\(d[x ...

  2. BZOJ.3611.[HEOI2014]大工程(虚树 树形DP)

    题目链接 要求的和.最大值.最小值好像都可以通过O(n)的树形DP做,总询问点数<=2n. 于是建虚树就可以了.具体DP见DP()函数,维护三个值sum[],mx[],mn[]. sum[]要开 ...

  3. bzoj 3611(洛谷 4103) [Heoi2014]大工程——虚树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3611 https://www.luogu.org/problemnew/show/P4103 ...

  4. bzoj 3611: [Heoi2014]大工程 虚树

    题目: 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 ...

  5. BZOJ 3611 [Heoi2014]大工程 ——虚树

    虚树第二题.... 同BZOJ2286 #include <map> #include <cmath> #include <queue> #include < ...

  6. bzoj 3611[Heoi2014]大工程 虚树+dp

    题意: 给一棵树 每次选 k 个关键点,然后在它们两两之间 新建 C(k,2)条 新通道. 求: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少 分析:较常 ...

  7. P4103 [HEOI2014]大工程

    题目 P4103 [HEOI2014]大工程 化简题目:在树上选定\(k\)个点,求两两路径和,最大的一组路径,最小的一组路径 做法 关键点不多,建个虚树跑一边就好了 \(sum_i\)为\(i\)子 ...

  8. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  9. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

随机推荐

  1. pom.xml文件设置

    一个相对完整的maven配置文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns= ...

  2. PMBOK

    项目章程的内容1. 基于项目干系人的需求和期望提出的要求.2. 项目必须满足的业务要求或产品需求.3. 项目的目的或项目立项的理由.4. 委派的项目经理及项目经理的权限级别.5. 概要的里程碑进度计划 ...

  3. 16/7/8_PHP-单引号和双引号的区别

    在PHP中,字符串的定义可以使用英文单引号' ',也可以使用英文双引号" ". 但是必须使用同一种单或双引号来定义字符串,如:'Hello World"和"He ...

  4. hbase迁移快照ExportSnapshot时遇到的错

    1.Cannot allocate memory 报错信息: Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x ...

  5. [集合]java中的 可变参数

    可变的参数类型,也称为不定参数类型.英文缩写是varargus,还原一下就是variable argument type.通过它的名字可以很直接地看出来,这个方法在接收参数的时候,个数是不定的. pu ...

  6. java _static 关键字

    • 在类中,用static声明的成员变量为静态成员变量 ,或者叫做: 类属性,类变量. • 它为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化, • 对于该类的所有对象来说,s ...

  7. 模板 - 可持久化无旋Treap

    空间消耗非常玄学,有多大开多大就完事了.其实是因为单次操作可能会有数次Merge和Split操作,按照下面的版本的话Merge和Split都进行复制,所以一次操作可能复制了4个版本. 四个函数式查询, ...

  8. Elasticsearch7.X 入门学习第三课笔记----search api学习(URI Search)

    原文:Elasticsearch7.X 入门学习第三课笔记----search api学习(URI Search) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出 ...

  9. 创建带标签页的MDI WinForms应用程序

    http://www.cnblogs.com/island/archive/2008/12/02/mditab.html 创建MDI应用程序 先创建”Windows窗体应用程序”解决方案Tabable ...

  10. 如何官网下载chrome谷歌浏览器离线安装包

    目录 1. 下载步骤 2. 将语言更改为中文 3. 插件 3.1. chrome 网上应用店 3.1.1. google-access-helper 4. 更新失败 1. 下载步骤 注意 需要梯子才能 ...