Luogu 4103 [HEOI2014]大工程
BZOJ 3611
明明在BZOJ上是$6s$的时限,怎么到Luogu上就变成$4s$了……
按照套路建出虚树,点之间的距离可以变成边权表示在虚树上,然后考虑如何树形$dp$。
最大值和最小值应当比较简单,类似于树形$dp$求树的直径的方法,设$f_x$表示$x$的子树中的关键点到$x$的最远距离,$g_x$则表示最近距离。
对于每一个$x$,如果$x$是一个关键点,则有$f_x = g_x = 0$,否则$f_x = -inf, g_x = inf$。
对于$x$的每一个儿子$y$,先用$f_x + f_y + val(x, y)$和$g_x + g_y + val(x, y)$更新$ans$,再用$f_x + val(x, y)$和$g_x + val(x, y)$更新$f_x$和$g_x$。
考虑一下怎么求所有边权的总和,我们发现一条边被计算的次数等于把这条边断开所分割成的两个联通块中的关键点的数量的乘积,那么它对答案的贡献就是这条边的边权乘上这个乘积。
其实除了根结点,所有的点都唯一对应了一条入边,我们先预处理一个点$x$,子树中关键点的数目$siz_x$,那么每一条入边$inEdge$的贡献就是$val(inEdge) * siz_x * (siz_{root} - x)$。
一开始犯了一个错误,就是不能把$1$强制作根,类似于点分治的时候需要减掉的贡献,这样会导致最长的路径变长,我们只要找到第一个入栈的点为$root$即可。
我的代码在$Luogu$上需要一发$O2$,感觉写欧拉序求$lca$会更好。
时间复杂度$O(nlogn)$。
Code:
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- using namespace std;
- typedef long long ll;
- const int N = 1e6 + ;
- const int Lg = ;
- const ll inf = 1LL << ;
- int n, qn, tot = , head[N], fa[N][Lg], dep[N];
- int rt, top = , sta[N * ], dfsc = , in[N], out[N], a[N * ], siz[N];
- ll sum, maxD, minD, f[N], g[N];
- bool vis[N], flag[N];
- struct Edge {
- int to, nxt, val;
- } e[N << ];
- inline void add(int from, int to, int val = ) {
- e[++tot].to = to;
- e[tot].val = val;
- e[tot].nxt = head[from];
- head[from] = tot;
- }
- namespace IOread {
- const int L = << ;
- char buffer[L], *S, *T;
- inline char Getchar() {
- if(S == T) {
- T = (S = buffer) + fread(buffer, , L, stdin);
- if(S == T) return EOF;
- }
- return *S++;
- }
- template <class T>
- inline void read(T &X) {
- char ch; T op = ;
- for(ch = Getchar(); ch > '' || ch < ''; ch = Getchar())
- if(ch == '-') op = -;
- for(X = ; ch >= '' && ch <= ''; ch = Getchar())
- X = (X << ) + (X << ) + ch - '';
- X *= op;
- }
- } using namespace IOread;
- bool cmp(int x, int y) {
- int dfx = x > ? in[x] : out[-x];
- int dfy = y > ? in[y] : out[-y];
- return dfx < dfy;
- }
- inline void swap(int &x, int &y) {
- int t = x; x = y; y = t;
- }
- template <typename T>
- inline void chkMax(T &x, T y) {
- if(y > x) x = y;
- }
- template <typename T>
- inline void chkMin(T &x, T y) {
- if(y < x) x = y;
- }
- void dfs(int x, int fat, int depth) {
- in[x] = ++dfsc, fa[x][] = fat, dep[x] = depth;
- for(int i = ; i <= ; i++)
- fa[x][i] = fa[fa[x][i - ]][i - ];
- for(int i = head[x]; i; i = e[i].nxt) {
- int y = e[i].to;
- if(y == fat) continue;
- dfs(y, x, depth + );
- }
- out[x] = ++dfsc;
- }
- inline int getLca(int x, int y) {
- if(dep[x] < dep[y]) swap(x, y);
- for(int i = ; i >= ; i--)
- if(dep[fa[x][i]] >= dep[y])
- x = fa[x][i];
- if(x == y) return x;
- for(int i = ; i >= ; i--)
- if(fa[x][i] != fa[y][i])
- x = fa[x][i], y = fa[y][i];
- return fa[x][];
- }
- inline int getDis(int x, int y) {
- int z = getLca(x, y);
- return dep[x] + dep[y] - * dep[z];
- }
- void dfs1(int x, int fat) {
- if(flag[x]) ++siz[x], g[x] = f[x] = ;
- else g[x] = inf, f[x] = -inf;
- for(int i = head[x]; i; i = e[i].nxt) {
- int y = e[i].to;
- if(y == fat) continue;
- dfs1(y, x);
- siz[x] += siz[y];
- chkMax(maxD, f[x] + f[y] + e[i].val);
- chkMax(f[x], f[y] + e[i].val);
- chkMin(minD, g[x] + g[y] + e[i].val);
- chkMin(g[x], g[y] + e[i].val);
- }
- }
- void dfs2(int x, int fat, int inEdge) {
- if(fat != )
- sum += 1LL * e[inEdge].val * (siz[rt] - siz[x]) * siz[x];
- for(int i = head[x]; i; i = e[i].nxt) {
- int y = e[i].to;
- if(y == fat) continue;
- dfs2(y, x, i);
- }
- }
- void solve() {
- int K, cnt; read(K);
- for(int i = ; i <= K; i++) {
- read(a[i]);
- if(!vis[a[i]]) {
- vis[a[i]] = ;
- flag[a[i]] = ;
- }
- }
- cnt = K;
- sort(a + , a + + K, cmp);
- for(int i = ; i < cnt; i++) {
- int now = getLca(a[i], a[i + ]);
- if(!vis[now]) {
- vis[now] = ;
- a[++cnt] = now;
- }
- }
- for(int cur = cnt, i = ; i <= cur; i++)
- a[++cnt] = -a[i];
- // if(!vis[1]) a[++cnt] = 1, a[++cnt] = -1, vis[1] = 1;
- sort(a + , a + + cnt, cmp);
- /* for(int i = 1; i <= cnt; i++)
- printf("%d ", a[i]);
- printf("\n"); */
- top = rt = ;
- for(int i = ; i <= cnt; i++) {
- if(a[i] > ) {
- sta[++top] = a[i];
- if(!rt) rt = a[i];
- } else {
- int x = sta[top--], y = sta[top];
- if(y) {
- int nowDis = getDis(x, y);
- add(x, y, nowDis), add(y, x, nowDis);
- }
- }
- }
- sum = 0LL, minD = inf, maxD = -inf;
- dfs1(rt, ), dfs2(rt, , );
- printf("%lld %lld %lld\n", sum, minD, maxD);
- tot = ;
- for(int i = ; i <= cnt; i++)
- if(a[i] > ) {
- vis[a[i]] = flag[a[i]] = ;
- head[a[i]] = siz[a[i]] = ;
- // f[a[i]] = -inf, g[a[i]] = inf;
- }
- }
- int main() {
- read(n);
- for(int x, y, i = ; i < n; i++) {
- read(x), read(y);
- add(x, y), add(y, x);
- }
- dfs(, , );
- tot = ; memset(head, , sizeof(head));
- for(read(qn); qn--; ) solve();
- return ;
- }
Luogu 4103 [HEOI2014]大工程的更多相关文章
- luogu P4103 [HEOI2014]大工程 虚树 + 树形 DP
Description 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通 ...
- bzoj 3611(洛谷 4103) [Heoi2014]大工程——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3611 https://www.luogu.org/problemnew/show/P4103 ...
- 洛谷4103 HEOI2014大工程(虚树+dp)
又是一道虚树好题啊 我们建出来虚树,然后考虑dp过程,我们分别令\(sum[x],mndis[x],mxdis[x],size[x]\)为子树内的路径长度和,最短链,最长链,子树内关键点个数. 对于一 ...
- 3611: [Heoi2014]大工程
3611: [Heoi2014]大工程 链接 分析: 树形dp+虚树. 首先建立虚树,在虚树上dp. dp:sum[i]为i的子树中所有询问点之间的和.siz[i]为i的子树中有多少询问点,mn[i] ...
- [BZOJ3611][Heoi2014]大工程
[BZOJ3611][Heoi2014]大工程 试题描述 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 ...
- bzoj 3611 [Heoi2014]大工程(虚树+DP)
3611: [Heoi2014]大工程 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 408 Solved: 190[Submit][Status] ...
- 【LG4103】[HEOI2014]大工程
[LG4103][HEOI2014]大工程 题面 洛谷 题解 先建虚树,下面所有讨论均是在虚树上的. 对于第一问:直接统计所有树边对答案的贡献即可. 对于第\(2,3\)问:记\(f[x]\)表示在\ ...
- P4103 [HEOI2014]大工程
题目 P4103 [HEOI2014]大工程 化简题目:在树上选定\(k\)个点,求两两路径和,最大的一组路径,最小的一组路径 做法 关键点不多,建个虚树跑一边就好了 \(sum_i\)为\(i\)子 ...
- BZOJ2286 [Sdoi2011]消耗战 和 BZOJ3611 [Heoi2014]大工程
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6371 Solved: 2496[Submit][Statu ...
随机推荐
- 速记JVM内存模型和垃圾回收策略
一.常用JVM参数 -Xms: 初始堆大小 -Xmx: 最大堆-Xmn: 新生代大小 -Xss: 栈容量 -PermSize: 方法区大小 -MaxPermSize: 最大方法区大小 -MaxDire ...
- Django上传文件的两种方式
基于form表单上传文件 HTML <h3>基于form表单的上传文件</h3> <form action="" method="post& ...
- 网络编程基础--IO模型
一 IO模型介绍: 背景 是 Linux环境下 的 network IO , Third Edition: The Sockets Networking ”,.2节“I/O Models ”,Stev ...
- 树莓派(Linux)与镜像源
树莓派学习笔记--修改树莓派软件源 1. linux 镜像源文件 >> vim /etc/apt/sources.list 可在树莓派官网 http://www.raspbian.org/ ...
- Zeroc Ice 负载均衡之Icegrid simple
最近学习Icestorm的replicated例子,在本地计算机上面跑通了,但在两台机器上(一台服务器192.168.0.113,一台客户端192.168.0.188),怎么都跑不通.上网求助,大家给 ...
- PLSQL Developer 攻略
.Net程序员学用Oracle系列(18):PLSQL Developer 攻略 1.功能说明及使用技巧 1.1.对象浏览器 1.2.SQL 窗口 1.3.测试窗口 1.4.命令窗口 1.5.图表 ...
- angular track by $index
这个东西配合删除数组时会出现永远删除uoloadPicArr这个数组的最后一个一项 其用法是主要用在当ng-repeat循环时如果其内部的dom元素有id属性存在 可以避免页面出现相同的id 只是一 ...
- 使用while 打印10~1,1~10
使用while 打印10~1,1~10 #!/bin/bash i= ));do echo $i ((i--)) done 答案:109876543210 i= ));do echo $i ((i++ ...
- 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器
服务器比较简陋,为了学习poll的使用,只向客户端回写一条html语句.启动服务器后,浏览器发起请求,服务端向浏览器写回html,响应字符串,然后可以看到,浏览器解析并显示 Hello Poll!. ...
- svn-clearup 报错的处理(Cleanup failed to process the following paths...)
在使用 svn 客户端执行操作失败后,执行 Clean up 操作也报错:Cleanup failed to process the following paths... ,一直不知道是什么原因.通常 ...