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

输入输出样例

输入样例#1: 复制

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

输出样例#1: 复制

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

说明

对于第 1,2 个点: n<=10000

对于第 3,4,5 个点: n<=100000,交通网络构成一条链

对于第 6,7 个点: n<=100000

对于第 8,9,10 个点: n<=1000000

对于所有数据, q<=50000并且保证所有k之和<=2*n

看到k的和小于2*n,于是立刻想到建虚树

建出虚树后就dp

size[x]表示x的子树中关键点数

f[x]表示x子树中路径的贡献和

Min[x]表示x子树中离x距离最小的关键点的距离

Max[x]表示最大的距离

最大值和求和很简单

最大值总是要取到叶子节点,虚树中叶子节点总是关键点

答案取当前Max[x]+Max[v]+边权w,然后Max[x]=max(Max[x],Max[v]+d)

求和就考虑一条边的贡献

一条边的贡献次数显然是size[v]*(k-size[v])

所以f[x]+=f[v]+size[v]*(k-size[v])*w

求最小值的话,Min[x]初值正无穷

如果是关键点就直接取它的子树路径最小值,否则就是它的两个儿子的子树路径最小值相加

如果是关键点,更新答案后Min[x]要清0,作为接下来的端点

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 typedef long long lol;
  8 const int N=2000005;
  9 struct Node
 10 {
 11     int next,to;
 12 }edge[N],edge2[N];
 13 int inf=1e9;
 14 int dep[N],fa[N][21],dfn[N],cnt,bin[25],head[N],head2[N],num,ed[N];
 15 int size[N],vis[N],Max[N],Min[N],k,M,ans1,ans2,n,Lca,a[N],s[N],top;
 16 lol f[N];
 17 int gi()
 18 {
 19     char ch=getchar();
 20     int x=0;
 21     while (ch<‘0‘||ch>‘9‘) ch=getchar();
 22     while (ch>=‘0‘&&ch<=‘9‘)
 23     {
 24         x=x*10+ch-‘0‘;
 25         ch=getchar();
 26     }
 27     return x;
 28 }
 29 bool cmp(int a,int b)
 30 {
 31     return dfn[a]<dfn[b];
 32 }
 33 void add(int u,int v)
 34 {
 35     num++;
 36     edge[num].next=head[u];
 37     head[u]=num;
 38     edge[num].to=v;
 39 }
 40 void add2(int u,int v)
 41 {
 42     if (u==v) return;
 43     num++;
 44     edge2[num].next=head2[u];
 45     head2[u]=num;
 46     edge2[num].to=v;
 47 }
 48 void dfs(int x,int pa)
 49 {int i;
 50     dep[x]=dep[pa]+1;
 51     dfn[x]=++cnt;
 52     for (i=1;bin[i]<=dep[x];i++)
 53     fa[x][i]=fa[fa[x][i-1]][i-1];
 54     for (i=head[x];i;i=edge[i].next)
 55     {
 56         int v=edge[i].to;
 57         if (v==pa) continue;
 58         fa[v][0]=x;
 59         dfs(v,x);
 60     }
 61     ed[x]=cnt;
 62 }
 63 int lca(int x,int y)
 64 {int as,i;
 65     if (dep[x]<dep[y]) swap(x,y);
 66     for (i=20;i>=0;i--)
 67     if (bin[i]<=dep[x]-dep[y])
 68     x=fa[x][i];
 69     if (x==y) return x;
 70     for (i=20;i>=0;i--)
 71     {
 72         if (fa[x][i]!=fa[y][i])
 73         {
 74             x=fa[x][i];y=fa[y][i];
 75         }
 76     }
 77     return fa[x][0];
 78 }
 79
 80 void dp(int x)
 81 {int i;
 82     size[x]=vis[x];
 83     Max[x]=0;Min[x]=inf;f[x]=0;
 84     for (i=head2[x];i;i=edge2[i].next)
 85     {
 86         int v=edge2[i].to,d=dep[v]-dep[x];
 87         dp(v);
 88         size[x]+=size[v];
 89         f[x]+=f[v]+1ll*size[v]*(k-size[v])*d;
 90         ans1=min(ans1,Min[x]+Min[v]+d);Min[x]=min(Min[x],Min[v]+d);
 91         ans2=max(ans2,Max[x]+Max[v]+d);Max[x]=max(Max[x],Max[v]+d);
 92     }
 93     if (vis[x]) ans1=min(ans1,Min[x]),ans2=max(ans2,Max[x]),Min[x]=0;
 94 }
 95 int main()
 96 {int i,u,v,j,q;
 97  cin>>n;
 98  bin[0]=1;
 99  for (i=1;i<=20;i++)
100  bin[i]=bin[i-1]*2;
101   for (i=1;i<=n-1;i++)
102    {
103       scanf("%d%d",&u,&v);
104       add(u,v);add(v,u);
105    }
106    dfs(1,0);
107    cin>>q;
108    while (q--)
109    {
110         k=gi();
111      M=k;
112         num=0;ans1=inf;ans2=0;
113         for (i=1;i<=k;i++)
114           a[i]=gi(),vis[a[i]]=1;
115         sort(a+1,a+k+1,cmp);
116         Lca=a[1];
117      for (i=2;i<=k;i++)
118        if (ed[a[i-1]]<dfn[a[i]])
119        a[++M]=lca(a[i-1],a[i]),Lca=lca(Lca,a[i]);
120        a[++M]=Lca;
121        sort(a+1,a+M+1,cmp);
122        M=unique(a+1,a+M+1)-a-1;
123        s[++top]=a[1];
124        for (i=2;i<=M;i++)
125        {
126            while (top&&ed[s[top]]<dfn[a[i]]) top--;
127            add2(s[top],a[i]);
128            s[++top]=a[i];
129        }
130        dp(Lca);
131        printf("%lld %d %d\n",f[Lca],ans1,ans2);
132         for (i=1;i<=M;i++)
133         vis[a[i]]=head2[a[i]]=0;
134    }
135 }

原文地址:https://www.cnblogs.com/Y-E-T-I/p/8454972.html

时间: 2024-10-01 22:46:11

[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 的最短路径. 现在国家有很多个计划,每个计划都是这样,

bzoj 3611: [Heoi2014]大工程

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

BZOJ3611 [Heoi2014]大工程 【虚树】

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

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

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

BZOJ 3611 HEOI2014 大工程 倍增LCA+单调栈+树形DP

题目大意:给定一棵树,m次询问,每次给出k个关键点,询问这k个点之间的两两距离和.最小距离和最大距离 n<=100W,m<=50000,Σk<=2*n 处理方法同2286 消耗战 地址见 http://blog.csdn.net/popoqqq/article/details/42493725 这个题的DP有些麻烦 因此我把要处理的节点单独拎出来做的DP 具体状态和转移见代码 #include <cstdio> #include <cstring> #includ

虚树(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))

【[HEOI2014]大工程 】

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

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[