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. CodeForces-831A-Unimodal Array (水题)

    题目链接 /* Name: Copyright: Author: Date: 2018/5/6 19:34:23 Description: */ #include <iostream> # ...

  2. Milking Time(DP)

    个人心得:一开始自己找状态,是这么理解的,只要前面一个满足就等于此时的值加上d(n-1),否则就是不挖此时的比较d(n-1)和 d(n-2)+cost,不过仔细一想忽略了很多问题,你无法确定n-2和此 ...

  3. LeetCode Maximum Average Subarray I

    原题链接在这里:https://leetcode.com/problems/maximum-average-subarray-i/description/ 题目: Given an array con ...

  4. 最长递增子序列 LIS 时间复杂度O(nlogn)的Java实现

    关于最长递增子序列时间复杂度O(n^2)的实现方法在博客http://blog.csdn.net/iniegang/article/details/47379873(最长递增子序列 Java实现)中已 ...

  5. SQL SERVER存储过程的几种示例

    1.常用系统存储过程及使用语法:exec sp_databases; --查看数据库exec sp_tables; --查看表exec sp_columns student;--查看列exec sp_ ...

  6. navicat链接远程数据库

    1.之前使用的是常规的连接方式 学习源头: https://jingyan.baidu.com/article/0aa2237573c1e688cc0d6427.html 这里的ip地址是服务器的ip ...

  7. Python学习笔记 - PostgreSQL的使用

    一.安装PostgreSQL模块 pip install psycopg2 有时候会失败,多安装2次就好了(我是第二次成功了). 二.数据库连接接口 由于Python统一了数据库连接的接口,所以psy ...

  8. HDOJ1010 (DFS+奇偶剪枝)

    Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  9. 一些js和JQuery的方法

    取值填值: js: get-- innerText, innerHTML, value;  set-- innerText=?, innerHTML=?, value=?; JQuery: get-- ...

  10. pom.xm首行报错Failure to transfer org.codehaus.plexus:plexus-components:pom:1.1.20

    从SVN导出一个Maven项目,pom.xml首行报错: Failure to transfer org.codehaus.plexus:plexus-components:pom:1.1.20 fr ...