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]大工程的更多相关文章

  1. luogu P4103 [HEOI2014]大工程 虚树 + 树形 DP

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

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

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

  3. 洛谷4103 HEOI2014大工程(虚树+dp)

    又是一道虚树好题啊 我们建出来虚树,然后考虑dp过程,我们分别令\(sum[x],mndis[x],mxdis[x],size[x]\)为子树内的路径长度和,最短链,最长链,子树内关键点个数. 对于一 ...

  4. 3611: [Heoi2014]大工程

    3611: [Heoi2014]大工程 链接 分析: 树形dp+虚树. 首先建立虚树,在虚树上dp. dp:sum[i]为i的子树中所有询问点之间的和.siz[i]为i的子树中有多少询问点,mn[i] ...

  5. [BZOJ3611][Heoi2014]大工程

    [BZOJ3611][Heoi2014]大工程 试题描述 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道.  我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上.  在 ...

  6. bzoj 3611 [Heoi2014]大工程(虚树+DP)

    3611: [Heoi2014]大工程 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 408  Solved: 190[Submit][Status] ...

  7. 【LG4103】[HEOI2014]大工程

    [LG4103][HEOI2014]大工程 题面 洛谷 题解 先建虚树,下面所有讨论均是在虚树上的. 对于第一问:直接统计所有树边对答案的贡献即可. 对于第\(2,3\)问:记\(f[x]\)表示在\ ...

  8. P4103 [HEOI2014]大工程

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

  9. BZOJ2286 [Sdoi2011]消耗战 和 BZOJ3611 [Heoi2014]大工程

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6371  Solved: 2496[Submit][Statu ...

随机推荐

  1. LeetCode 361. Bomb Enemy

    原题链接在这里:https://leetcode.com/problems/bomb-enemy/description/ 题目: Given a 2D grid, each cell is eith ...

  2. ubuntu 迁移部分 / 目录下的存储空间到 /home目录

    状况:当时给系统分区的时候,home和根目录都是25GB左右,突然发现home 目录不够用了,于是决定进行将根目录的部分空间挪移到home下去 主要方法:使用Gparted的LIve USB的方法. ...

  3. AFN 请求数据https

    第一步: 导入afn库 第二步: 在pch中添加 #import <SystemConfiguration/SystemConfiguration.h> #import <Mobil ...

  4. HTML5通信

    跨文档消息传输 HTML5中提供了在网页文档之间互相接收与发送信息的功能.使用这个功能只要获取到网页所在窗口对象的实例,无论是否同源都可以实现跨域通信.经常用于不同frame之间的通信. 当我们想要接 ...

  5. Java-API-Package:java.math

    ylbtech-Java-API-Package:java.math 1.返回顶部 1. Package java.math Provides classes for performing arbit ...

  6. python风格指南

    还在让别人一眼就看出你是一只野生程序猿嘛,快来看看谷歌的python风格指南提升逼格吧! http://zh-google-styleguide.readthedocs.io/en/latest/go ...

  7. java 基础知识学习

    1:  JDK VS  JRE JDK: java开发工具包,提供编译和运行环境,将java编译成字节码文件,即.class文件.JDK有三个版本: j2SE,  J2EE,  j2me jre: 运 ...

  8. java基础知识(15)----StringBuffer与StringBuilder

    StringBuffer字符串缓冲区: 构造一个其中不带字符的字符串缓冲区,初始容量为 16 个字符.特点:1:可以对字符串内容进行修改.2:是一个容器.3:是可变长度的.4:缓冲区中可以存储任意类型 ...

  9. PowerDesigner中CDM和PDM如何定义外键关系

    有A.B两张表(实体),各自有id作为主键,两表是一一对应关系.但略有不同: A表一条记录可以对应0或1条B表记录,B表一条记录必须对应唯一条A表记录. 这样的关系如何在CDM或PDM中定义? 在最后 ...

  10. 简单的windows作业管理(自己也没弄透彻)

    先把代码贴出来,以后有时间再研究!简单的说,作业就相当于沙箱,可以使程序在一定范围内活动. #include "stdafx.h"#include "windows.h& ...