10.4 国庆 环形dp与基环树笔记

1.知识点

环形dp

环形 dp 的概念

• 环形dp与基环树在许多环形结构的问题中,我们可以在环中从某个位置把环断开,把这个环变成线性的,然后进行 \(dp\) 等操作。
• 把能通过上述操作解决的环形问题称作 "可拆解的环形问题" 。

环形 dp 的两种策略

• 第一次在任意位置把环断开成链,按照线性问题求解;第二次通过适当的条件和赋值,保证计算出的状态等价于把断开的位置强制相连。
• 把环断成链,然后复制一倍在末尾

基环树

\(n\) 个点的树共有 \(n - 1\) 条边,如果再加上一条边,即 \(n\) 个点 \(n\) 条边,那树中必然会出现一个环。
• 把这种 \(n\) 个点 \(n\) 条边的联通无向图,即刚好包含一个环的图,称作 "基环树"。
• 如果不保证连通,那么 \(n\) 个点 \(n\) 条边的无向图也可能是若干棵基环树组成的森林,简称 "基环树森林"。

外向树

\(n\) 个点 \(n\) 条边,每个点有且仅有 \(1\) 条入边的有向图,被称为 "外向树"。
\(n\) 个点 \(n\) 条边,每个点有且仅有 \(1\) 条出边的有向图,被称为 "内向树"。

基环树的直径

• 基环树中最长的简单路径被称为基环树的最长链,其长度被称为基环树的直径。

基环树找环

• 自己一个很神奇的方法:

  1. 预处理出每个点的 \(father\)
  2. 从某个点开始,一直往它的 \(father\) 走,边走边做 \(vis\) 标记,当走到一个已经访问过的节点时,便找到了环。

Code:

int find(int u)//从 u 点开始找环
{
	vis[u] = true;
	node = u;
	while (!vis[fa[node]])
	{
		node = fa[node];
		vis[node] = true;
	}
	return node;
	//node 为环的一点
}

T1

Link

\(n\) 个点,\(m\) 条边。每次从任意一个点出发,每次可以走向一个没有访问过的点,或者沿着第一次访问这个点时经过的边后退到上一个点。
• 每到达一个点时,把这个点的编号记录下来,这样最后会组成一个长度为 \(n\) 的序列,期望这个序列的字典序最小,求出这个序列。
• 对于 \(60\%\) 的数据,满足 \(n \leq 5000\), \(m = n - 1\)
• 对于 \(40\%\) 的数据,满足 \(n \leq 5000\)\(m = n\)

Solution

• 数据范围提示了做法 /kk。
\(60 pts\) 就是白送的,容易可以发现一个性质:到达一个点时,一定要遍历完它的子树后才能返回。
• 题目要求字典序最小,于是用 \(vector\) 存图,对每个点的子树内进行排序,最后直接 \(dfs\) 即可。
• 而对于另外的 \(40 pts\),因为 \(m = n\),所以这是一棵基环树。
• 手玩一下基环树的样例可以发现,一定有一条边是无法经过的,我们枚举这条边,然后 \(dfs\) 即可。
• ps:加强版无法通过,可能是 \(vector\) 常数太大了/bx/bx。

T2

Link

咕咕咕。

T3

Link

\(n\) 个人,每个人都有一个价值,并且每个人都有一个自己讨厌的人(保证不为自己),要求你从这 \(n\) 个人里选出若干个人,满足:

  1. 每个人与他讨厌的人不能一同选择
  2. 选出这若干个人的价值和最大
    • 求出价值和。
    \(n \leq 10^6\)

Solution

• 把每个人都向他讨厌的人连一条边,那便是 \(n\) 个点 \(n\) 条边的基环树。
• 先找到环,并把它断开,然后我们跑两遍树形 \(dp\),求这两遍 \(dp\) 的最大值。
\(dp\) 的话就不多讲了,就是没有上司的舞会裸题,甚至直接 \(copy\) 都行/bx/bx。

T4

Link

\(n\) 种物品,每个物品可以限制另一种物品,需要你从中取出若干个物品,满足:

  1. 每个物品与它限制的物品不能一起被取出
  2. 选出的物品尽量多
    • 求出能选择的最多数量
    \(n \leq 10^6\)

Solution

• 与上题类似。\(dp\) 时多加了对 \(y\) 选边的限制。

T5

Link

• 题意自己看。/bx

Solution

• 一个简单的环形 \(dp\)
• 我们定义 \(dp[i][j][k]\) 表示当前走到 \(i\),卖萌了 \(j\) 次,当前状态是 \(k\),(\(k = 0\) 表示当前没有卖萌,\(k = 1\) 表示现在正在卖萌)
• 那么有方程

f[i][j][0]=max(f[i - 1][j][0],f[i - 1][j][1])
f[i][j][1]=max(f[i - 1][j - 1][0], f[i - 1][j - 1][1] + u[i])

• 因为是环形,先跑一遍 \(dp\),然后把 \(1, n\) 都固定正在卖萌,然后再跑一遍 \(dp\),取最大值。

T6

Link

\(N\) 座仓库,分布在一个 环形 的公路上,编号为 \(1 - N\),编号为 \(i\) 和编号为 \(j\) 的仓库之间的距离定义为 \(dist(i, j) = min(|i - j|, N - |i - j|)\)
• 编号为 \(i\) 仓库的储存量为 \(a_i\),在两个仓库间运货的价值为 \(a_i + a_j + dist(i, j)\)
• 求出哪两座仓库之间运送货物需要的代价最大。

Solution

• 由于是环形,所以用断环为链的方法,把这个环断开,然后进行线性 \(dp\)
\(\mathcal{O}(N ^ 2)\) 的暴力显然很好做,但是 \(N \leq 10^6\)
• 首先推出式子 \(f_{i, j} = max\{(a_i + i) + (a_j - j)\}\)
• 当 \(i\) 固定时,\((a_i + i)\) 就确定了下来,然后考虑 \(a_j - j\) 的最大值即可。
• 这一步用单调队列优化,于是时间复杂度变成 \(\mathcal{O}(N)\)

Code:

for (int i = 2; i <= (n << 1); i ++ )
{
	while (q.size() && q.front() < i - m) q.pop_front();
	ans = max(ans, s[q.front()] - q.front() + s[i] + i);
	while (q.size() && s[q.back()] - q.back() < s[i] - i) q.pop_back();
	q.push_back(i);
}
posted @ 2023-10-04 11:36  恋暗  阅读(102)  评论(0)    收藏  举报