Solution -「树上杂题?」专练
主要是记录思路,不要被刚开始错误方向带偏了 www
「CF1110F」Nearest Leaf
特殊性质:先序遍历即为 \(1 \to n\),可得出:叶子节点编号递增或可在不改变树形态的基础上调整为递增。
这样就可找出区间 \([l, r]\) 中的叶子节点有哪些了,预处理深度,暴力 \(O(n ^ 2)\)。
考虑柿子 \(\min \{\mathrm{d} (y) - \mathrm{d} (\mathrm{f} (x))\}\),其中 \(d(x)\) 表示深度,\(f(x)\) 表示父亲,\(y\) 为枚举的叶子,\(x\) 为定点。
要使答案最小,即求 \(\min \{\mathrm{d}(y)\}\),这不线段树???哦 GG,区间 \([l, r]\) 显然有可能在定点 \(x\) 子树外。
那如果我们把离线询问挂树上,考虑动态维护线段树,保证遍历到一个节点时,线段树的长相是正确的,好像就能做了?即:
- 遇到一个节点,我们就把子树内 \(-\),子树外 \(+\),然后直接处理查询。
- 线段树维护区间加和区间最小值。
- 注意极大值的取值,注意回溯的时候要复原。
过了。
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
LL Abs (LL x) { return x < 0 ? -x : x; }
LL Max (LL x, LL y) { return x > y ? x : y; }
LL Min (LL x, LL y) { return x < y ? x : y; }
int Read () {
int x = 0, k = 1;
char s = getchar ();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar ();
}
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
}
void Write (LL x) {
if (x < 0)
putchar ('-'), x = -x;
if (x > 9)
Write (x / 10);
putchar (x % 10 + '0');
}
void Print (LL x, char s) { Write (x), putchar (s); }
const LL Inf = 1e16;
const int Maxn = 5e5 + 5;
struct Segment_Tree {
struct Segment_Node {
int l, r;
LL Lazy, Val;
#define Lson p << 1
#define Rson p << 1 | 1
Segment_Node () {}
Segment_Node (int L, int R, LL La, LL V) { l = L, r = R, Lazy = La, Val = V; }
} Tr[Maxn << 2];
void Make_Tree (int p, int l, int r) {
Tr[p].Val = Inf, Tr[p].l = l, Tr[p].r = r, Tr[p].Lazy = 0;
if (l == r)
return ;
int Mid = (l + r) >> 1;
Make_Tree (Lson, l, Mid), Make_Tree (Rson, Mid + 1, r);
}
void Pull (int p) { Tr[p].Val = Min (Tr[Lson].Val, Tr[Rson].Val); }
void Push (int p) {
if (Tr[p].Lazy) {
Tr[Lson].Val += Tr[p].Lazy, Tr[Rson].Val += Tr[p].Lazy;
Tr[Lson].Lazy += Tr[p].Lazy, Tr[Rson].Lazy += Tr[p].Lazy;
Tr[p].Lazy = 0;
}
}
void Update (int p, int l, int r, LL x) {
if (l <= Tr[p].l && Tr[p].r <= r) {
Tr[p].Lazy += x, Tr[p].Val += x;
return ;
}
Push (p);
int Mid = (Tr[p].l + Tr[p].r) >> 1;
if (l <= Mid)
Update (Lson, l, r, x);
if (r > Mid)
Update (Rson, l, r, x);
Pull (p);
}
LL Query (int p, int l, int r) {
if (l <= Tr[p].l && Tr[p].r <= r)
return Tr[p].Val;
Push (p);
LL Res = Inf;
int Mid = (Tr[p].l + Tr[p].r) >> 1;
if (l <= Mid)
Res = Min (Res, Query (Lson, l, r));
if (r > Mid)
Res = Min (Res, Query (Rson, l, r));
return Res;
}
#undef Lson
#undef Rson
} Tree;
struct Query {
int l, r, Id;
Query () {}
Query (int L, int R, int I) { l = L, r = R, Id = I; }
};
struct Edge {
int v, w;
Edge () {}
Edge (int V, int W) { v = V, w = W; }
friend bool operator < (Edge x, Edge y) { return x.v < y.v; }
};
int Size[Maxn], n, m;
LL Res[Maxn], Dep[Maxn];
vector <Query> q[Maxn];
vector <Edge> Graph[Maxn];
void Add_Edge (int u, int v, int w) { Graph[u].push_back (Edge (v, w)); }
void Dfs (int u) {
Size[u] = 1;
for (int i = 0, v; i < Graph[u].size (); i++)
v = Graph[u][i].v, Dep[v] = Dep[u] + Graph[u][i].w, Dfs (v), Size[u] += Size[v];
if (Graph[u].size () == 0)
Tree.Update (1, u, u, Dep[u] - Inf);
}
void Calc (int u) {
for (int i = 0; i < q[u].size (); i++)
Res[q[u][i].Id] = Tree.Query (1, q[u][i].l, q[u][i].r);
for (int i = 0, v, w; i < Graph[u].size (); i++) {
v = Graph[u][i].v, w = Graph[u][i].w;
Tree.Update (1, v, v + Size[v] - 1, -w);
if (v > 1)
Tree.Update (1, 1, v - 1, w);
if (v + Size[v] - 1 < n)
Tree.Update (1, v + Size[v], n, w);
Calc (v);
Tree.Update (1, v, v + Size[v] - 1, w);
if (v > 1)
Tree.Update (1, 1, v - 1, -w);
if (v + Size[v] - 1 < n)
Tree.Update (1, v + Size[v], n, -w);
}
}
int main () {
n = Read (), m = Read ();
for (int i = 2, p, w; i <= n; i++)
p = Read (), w = Read (), Add_Edge (p, i, w);
for (int i = 1; i <= n; i++)
sort (Graph[i].begin (), Graph[i].end ());
for (int i = 1, x, l, r; i <= m; i++)
x = Read (), l = Read (), r = Read (), q[x].push_back (Query (l, r, i));
Tree.Make_Tree (1, 1, n), Dfs (1), Calc (1);
for (int i = 1; i <= m; i++)
Print (Res[i], '\n');
return 0;
}
「CF633F」The Chocolate Spree
不相交的两条链的最大权值和,难道直接通过直径去扩展?或者直接硬 DP
?
考虑分讨四种情况:Dp[u][0/1/2/3]
分别表示(均在子树内)选了两条链 / 选了一条链 / 选了两条链,其中一条端点是 \(u\) / 选了一条链端点是 \(u\)。
以下转移均考虑遍历顺序(雾。
- 对于
Dp[u][0]
- \(v\) 选了两条,直接更新,
Dp[u][0] = Max (Dp[u][0], Dp[v][0])
- \(v\) 选了一条,故 \(u\) 再选一条,
Dp[u][0] = Max (Dp[u][0], Dp[v][1] + Dp[u][1])
- \(v\) 选了两条其中一条端点是 \(v\),扩展,
Dp[u][0] = Max (Dp[u][0], Dp[v][2] + w[u])
- \(v\) 选了一条且端点是 \(v\),扩展,
Dp[u][0] = Max (Dp[u][0], Dp[v][3] + w[u] + Dp[u][1] - w[u])
- 可以两条接起来一条,
Dp[u][0] = Max (Dp[u][0], Max (Dp[v][2] + Dp[u][3], Dp[u][2] + Dp[v][3])
- \(v\) 选了两条,直接更新,
- 对于
Dp[u][1]
- \(v\) 选了一条,直接更新,
Dp[u][1] = Max (Dp[u][1], Dp[v][1])
- \(v\) 选了一条且端点是 \(v\),扩展,
Dp[u][1] = Max (Dp[u][1], Dp[v][3] + w[u])
- 可以两条接起来,
Dp[u][0] = Max (Dp[u][0], Dp[v2][3] + Dp[v2][3] + w[u])
- \(v\) 选了一条,直接更新,
- 对于
Dp[u][2]
- \(v\) 选了一条,则 \(u\) 再选一条端点是 \(u\),
Dp[u][2] = Max (Dp[u][2], Dp[v][1] + Dp[u][3])
- \(v\) 选了两条且其中一条端点是 \(v\),扩展,
Dp[u][2] = Max (Dp[u][2], Dp[v][2] + w[u])
- \(v\) 选了一条且端点是 \(v\),扩展然后再选一条,或者不扩展再选一条端点为 \(u\),
Dp[u][2] = Max (Dp[u][2], Max (Dp[v][3] + w[u] + Dp[u][1] - w[u], Dp[v][3] + Dp[u][3]))
- \(v\) 选了一条,则 \(u\) 再选一条端点是 \(u\),
- 对于
Dp[u][3]
- \(v\) 选了一条且端点是 \(v\),扩展,
Dp[u][3] = Max (Dp[u][3], Dp[v][3] + w[u])
- \(v\) 选了一条且端点是 \(v\),扩展,
然后一阵删删改改过掉了,或许和上面的整理有点出入?看代码吧。
#include <cstdio>
#include <vector>
using namespace std;
typedef long long LL;
LL Abs (LL x) { return x < 0 ? -x : x; }
LL Max (LL x, LL y) { return x > y ? x : y; }
LL Min (LL x, LL y) { return x < y ? x : y; }
int Read () {
int x = 0, k = 1;
char s = getchar ();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar ();
}
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
}
void Write (LL x) {
if (x < 0)
putchar ('-'), x = -x;
if (x > 9)
Write (x / 10);
putchar (x % 10 + '0');
}
void Print (LL x, char s) { Write (x), putchar (s); }
const int Maxn = 1e5 + 5;
int w[Maxn];
LL Dp[Maxn][4];
vector <int> Graph[Maxn];
void Add_Edge (int u, int v) { Graph[u].push_back (v), Graph[v].push_back (u); }
void Tree_Dp (int u, int Fa) {
Dp[u][1] = Dp[u][3] = w[u];
LL Fir = 0, Sec = 0, Ma = 0;
for (int i = 0, v; i < Graph[u].size (); i++) {
v = Graph[u][i];
if (v == Fa)
continue;
Tree_Dp (v, u);
Dp[u][0] = Max (Dp[u][0], Dp[v][0]);
Dp[u][0] = Max (Dp[u][0], Dp[v][1] + w[u]);
Dp[u][0] = Max (Dp[u][0], Dp[v][2] + w[u]);
Dp[u][0] = Max (Dp[u][0], Dp[v][3] + w[u]);
if (i > 0) {
Dp[u][0] = Max (Dp[u][0], Dp[v][1] + Dp[u][1]);
Dp[u][0] = Max (Dp[u][0], Dp[v][1] + Dp[u][3]);
if (Ma)
Dp[u][0] = Max (Dp[u][0], Ma + w[u] + Dp[v][3]);
Dp[u][0] = Max (Dp[u][0], Dp[v][2] + Dp[u][3]);
Dp[u][0] = Max (Dp[u][0], Dp[v][3] + Dp[u][1]);
Dp[u][0] = Max (Dp[u][0], Dp[v][3] + Dp[u][3]);
Dp[u][0] = Max (Dp[u][0], Dp[v][3] + Dp[u][2]);
}
Dp[u][2] = Max (Dp[u][2], Dp[v][1] + w[u]);
Dp[u][2] = Max (Dp[u][2], Dp[v][2] + w[u]);
Dp[u][2] = Max (Dp[u][2], Dp[v][3] + w[u]);
if (i > 0) {
Dp[u][2] = Max (Dp[u][2], Dp[v][1] + Dp[u][3]);
if (Ma)
Dp[u][2] = Max (Dp[u][2], Ma + w[u] + Dp[v][3]);
Dp[u][2] = Max (Dp[u][2], Dp[v][3] + Dp[u][3]);
}
if (Dp[v][1] > Ma)
Ma = Dp[v][1];
if (Dp[v][3] > Fir)
Sec = Fir, Fir = Dp[v][3];
else if (Dp[v][3] > Sec)
Sec = Dp[v][3];
if (Fir && Sec)
Dp[u][1] = Max (Dp[u][1], Fir + Sec + w[u]);
Dp[u][1] = Max (Dp[u][1], Dp[v][1]);
Dp[u][1] = Max (Dp[u][1], Dp[v][3] + w[u]);
Dp[u][3] = Max (Dp[u][3], Dp[v][3] + w[u]);
}
}
int main () {
int n = Read ();
for (int i = 1; i <= n; i++)
w[i] = Read ();
for (int i = 1, u, v; i < n; i++)
u = Read (), v = Read (), Add_Edge (u, v);
Tree_Dp (1, -1);
// for (int i = 1; i <= n; i++)
// printf ("%lld %lld %lld %lld\n", Dp[i][0], Dp[i][1], Dp[i][2], Dp[i][3]);
Print (Dp[1][0], '\n');
return 0;
}
「CF1120D」Power Tree
将所有叶子按 DFS
序拉成一个序列,记点 \(u\) 子树内最靠左的叶子为 \(l_u\),最靠右的叶子为 \(r_u\)。不难发现,题目中一次操作等价于在 \([l_u, r_u]\) 上做区间修改,也可转换为在差分序列上单点修改。
即是说对于点 \(u\),我们可以将 \(l_u\) 处加上 \(d\),在 \(r_u + 1\) 处减去 \(d\),目标是使所有点都为 \(0\)。
如果将 \(l_u\) 和 \(r_u + 1\) 进行连边,边权为 \(w_u\)。如果原树上所有节点的对应点都可以跑到新节点 \(n + 1\) 上,则一定可以得到符合题意的最终状态(逆向树上差分?
题目转换为求哪些点可能出现在无向图最小生成树上,Kruskal
判一下即可。
出现了!写并查集不写路径压缩的大憨憨!
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
int Abs (int x) { return x < 0 ? -x : x; }
int Max (int x, int y) { return x > y ? x : y; }
int Min (int x, int y) { return x < y ? x : y; }
int Read () {
int x = 0, k = 1;
char s = getchar ();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar ();
}
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
}
void Write (LL x) {
if (x < 0)
putchar ('-'), x = -x;
if (x > 9)
Write (x / 10);
putchar (x % 10 + '0');
}
void Print (LL x, char s) { Write (x), putchar (s); }
const int Inf = 1e9;
const int Maxn = 2e5 + 5;
struct Node {
int l, r;
Node () {}
Node (int L, int R) { l = L, r = R; }
} q[Maxn];
struct Edge {
int u, v, w, Pos;
Edge () {}
Edge (int U, int V, int W, int P) { u = U, v = V, w = W, Pos = P; }
friend bool operator < (Edge x, Edge y) { return x.w < y.w; }
} e[Maxn];
bool Vis[Maxn];
vector <int> Graph[Maxn];
int w[Maxn], Dfn[Maxn], Fa[Maxn], Cnt;
void Add_Edge (int u, int v) { Graph[u].push_back (v), Graph[v].push_back (u); }
void Make_Tree (int n) {
for (int i = 1; i <= n; i++)
Fa[i] = i;
}
int Find_Set (int x) { return Fa[x] == x ? x : Fa[x] = Find_Set (Fa[x]); }
void Dfs (int u, int Fa) {
q[u].l = Inf, q[u].r = -Inf;
for (int i = 0, v; i < Graph[u].size (); i++) {
v = Graph[u][i];
if (v == Fa)
continue;
Dfs (v, u), q[u].l = Min (q[u].l, q[v].l), q[u].r = Max (q[u].r, q[v].r);
}
if (Graph[u].size () == 1 && u != 1)
q[u].l = q[u].r = ++Cnt;
e[u] = Edge (q[u].l, q[u].r + 1, w[u], u);
}
int main () {
int n = Read ();
for (int i = 1; i <= n; i++)
w[i] = Read ();
for (int i = 1, u, v; i < n; i++)
u = Read (), v = Read (), Add_Edge (u, v);
Dfs (1, -1), sort (e + 1, e + n + 1);
Make_Tree (Cnt + 1), Cnt++;
LL Res = 0;
int Tot = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = l;
while (r + 1 <= n && e[r].w == e[r + 1].w)
r++;
for (int i = l, u, v; i <= r; i++) {
u = Find_Set (e[i].u), v = Find_Set (e[i].v);
if (u != v)
Vis[e[i].Pos] = true, Tot++;
}
for (int i = l, u, v; i <= r; i++) {
u = Find_Set (e[i].u), v = Find_Set (e[i].v);
if (u != v)
Fa[v] = u, Res += e[i].w;
}
}
Print (Res, ' '), Print (Tot, '\n');
for (int i = 1; i <= n; i++)
if (Vis[i])
Print (i, ' ');
putchar ('\n');
return 0;
}
Solution -「树上杂题?」专练的更多相关文章
- LOJ6003 - 「网络流 24 题」魔术球
原题链接 Description 假设有根柱子,现要按下述规则在这根柱子中依次放入编号为的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法 ...
- LOJ6002 - 「网络流 24 题」最小路径覆盖
原题链接 Description 求一个DAG的最小路径覆盖,并输出一种方案. Solution 模板题啦~ Code //「网络流 24 题」最小路径覆盖 #include <cstdio&g ...
- LOJ6001 - 「网络流 24 题」太空飞行计划
原题链接 Description 有个实验和个仪器,做实验有报酬买仪器有花费.每个实验都需要一些仪器,求最大净收益(实验报酬仪器花费),并输出一组方案. Solution 实验向所需仪器连边,实验的点 ...
- Libre 6006 「网络流 24 题」试题库 / Luogu 2763 试题库问题 (网络流,最大流)
Libre 6006 「网络流 24 题」试题库 / Luogu 2763 试题库问题 (网络流,最大流) Description 问题描述: 假设一个试题库中有n道试题.每道试题都标明了所属类别.同 ...
- loj #6122. 「网络流 24 题」航空路线问题
#6122. 「网络流 24 题」航空路线问题 题目描述 给定一张航空图,图中顶点代表城市,边代表两个城市间的直通航线.现要求找出一条满足下述限制条件的且途经城市最多的旅行路线. 从最西端城市出发,单 ...
- [LOJ#6002]「网络流 24 题」最小路径覆盖
[LOJ#6002]「网络流 24 题」最小路径覆盖 试题描述 给定有向图 G=(V,E).设 P 是 G 的一个简单路(顶点不相交)的集合.如果 V 中每个顶点恰好在 P 的一条路上,则称 P 是 ...
- liberOJ#6006. 「网络流 24 题」试题库 网络流, 输出方案
#6006. 「网络流 24 题」试题库 题目描述 假设一个试题库中有 n nn 道试题.每道试题都标明了所属类别.同一道题可能有多个类别属性.现要从题库中抽取 m mm 道题组成试卷.并要求 ...
- LOJ6000 - 「网络流 24 题」搭配飞行员
原题链接 题意简述 求二分图的最大匹配. 题解 这里写的是匈牙利算法. 表示节点的当前匹配. 为真表示在这一轮匹配中,无法给节点一个新的匹配.所以如果为真就不用再dfs它了,直接continue就好. ...
- LibreOJ #6014. 「网络流 24 题」最长 k 可重区间集
#6014. 「网络流 24 题」最长 k 可重区间集 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 ...
随机推荐
- 记一次burp suite文件上传漏洞实验
一·文件上传漏洞概念 文件上传漏洞是指 Web 服务器允许用户在没有充分验证文件名称.类型.内容或大小等内容的情况下将文件上传到其文件系统.未能正确执行这些限制可能意味着 即使是基本的图像上传功能也可 ...
- 浅谈Nginx性能调优
点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! Linux系统参数优化 下文中提到的一些配置,需要较新的 ...
- 解析Java-throw抛出异常详细过程
摘要:Java有3种抛出异常的形式:throw.throws.系统自动抛异常. 本文分享自华为云社区<Java-throw异常详解以及过程>,作者: gentle_zhou . 首先,我们 ...
- 图文详解 HDFS 的工作机制及其原理
大家好,我是大D. 今天开始给大家分享关于大数据入门技术栈--Hadoop的学习内容. 初识 Hadoop 为了解决大数据中海量数据的存储与计算问题,Hadoop 提供了一套分布式系统基础架构,核心内 ...
- spring boot rest controller 自定义反序列化 Date 格式
@JsonFormat(pattern = DatePattern.NORM_DATE_PATTERN) private Date time;
- 【算法】桶排序(Bucket Sort)(九)
桶排序(Bucket Sort) 桶排序是计数排序的升级版.它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定.桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将 ...
- 153. Find Minimum in Rotated Sorted Array - LeetCode
Question 153. Find Minimum in Rotated Sorted Array Solution 题目大意:给一个按增序排列的数组,其中有一段错位了[1,2,3,4,5,6]变成 ...
- 编程语言与python与pycharm的下载
目录 编程语言的发展史 编程语言的分类 python解释器 python解释器的下载与安装 环境变量 执行python程序方式 pycharm编辑器 编程语言的发展史 机器语言是最开始的编程语言,之后 ...
- MIT 6.824(Spring 2020) Lab1: MapReduce 文档翻译
首发于公众号:努力学习的阿新 前言 大家好,这里是阿新. MIT 6.824 是麻省理工大学开设的一门关于分布式系统的明星课程,共包含四个配套实验,实验的含金量很高,十分适合作为校招生的项目经历,在文 ...
- Docker的网络
概述 docker使用Linux桥接网卡,在宿主机虚拟一个docker容器网桥(docker0),docker启动一个容器时会根 据docker网桥的网段分配给容器一个IP地址,称为Container ...