[SDOI2010] 城市规划 题解
前言
题目链接:洛谷。
题意简述
树套环上求至少间隔两个位置的最大独立集。
(树套环,即树上每个结点都是一个结点或环)
题目分析
将题目拆解成树上 DP 和环上 DP 即可。用 tarjan 缩点就行。
树上 DP
先来看看树上 DP。
显然每个点有三个状态:不选中且周围没选中、选中、不选中但在选中的点的旁边。记成 \(f[yzh][0/1/2]\)。设 \(xym\) 是 \(yzh\) 的一个孩子。
先来看看 \(f[yzh][0]\)。由于她不选中,且不在一个选中的旁边,可以从孩子的 \(0\)、\(2\) 状态转移而来。
\]
\(f[yzh][1]\)。由于她选中,那么孩子在之前必须没被选中,且不在选中的点的旁边,即只能从孩子的 \(0\) 状态转移而来。注意加上她的价值。
\]
\(f[yzh][2]\)。她能且只能从一个孩子的 \(1\) 状态转移而来,其他儿子要么是 \(0\) 状态,要么是 \(2\) 状态。取最大值。
\]
这样,得到 \(20 \%\) 树的部分分。
环上 DP
先把环“拉下来”,即把环按照一定顺序存下来。类似于树,记 \(g[i][0/1/2]\) 表示考虑到了环上前 \(i\) 个点,第 \(i\) 个点状态下最大价值,再记 \(f[i][0/1/2]\) 表示从第 \(i\) 个点,走到子树里,树形 DP 的结果。先不考虑其他的,来看看转移方程。
\(g[yzh][0]\)。在树里,她不能被选中,即 \(f[yzh][0]\);在环里,前一个位置不能是 \(1\) 状态。
\]
\(g[yzh][1]\)。不妨把 \(yzh\) 的价值计算到树里。环里前一个位置之前不能选。那么就是 \(f[yzh][1]\) 和 \(g[yzh - 1][0]\) 的和。
\]
\(g[yzh][2]\)。这里需要分类讨论,这个 \(2\) 状态可能是应为环里前一个位置是 \(1\) 状态,或者树里达到了 \(2\) 状态。取最大值即可。
\]
以上就是转移方程。重点在环的特殊性。所以套路化地想到,把左侧强制设为某一个状态,那么右侧只有部分状态是有效的。为了之后树形 DP,我们不妨把这个环的“上顶点”移到我们“拉下来”的数组的右侧。
- 若左侧是 \(0\) 状态。
那么右侧三个状态都合法。 - 若左侧是 \(1\) 状态。
那么右侧只能是环上 DP 出来的 \(0\) 状态,并且在反映到树形 DP 上时是 \(2\) 状态。 - 若左侧是 \(2\) 状态。
那么右侧 \(0\) 状态或 \(2\) 状态都是合法的。
这样看起来没有问题了。但是,还是会错,给出我对拍出来的一组数据:
10 11
5 1 1 1 3 5 7 2 8 9
1 2
2 3
3 4
4 5
5 1
6 7
7 8
8 9
9 10
10 6
1 6
答案显然是 \(13\),可是我们输出了 \(14\)。(好吧,可能有那么一点不显然)
为什么呢?问题就出在了我们对左侧 \(0\) 状态的考虑。这个位置的右边可能后来被选中了,而在树形 DP 上传答案的时候,右侧也是选中的,这样两者中间只间隔了 \(1\) 个,冲突了。
如何解决呢?再强制以下就好了:一遍强制左侧右边那个位置不能选中,就可以放心转移了。
代码
代码实现很清晰。略去了快读。注意用 vector
可能会含泪 MLE \(90\) 分,用链式前向星即可。可能有之前卡空间的遗物?
#include <vector>
#include <array>
#include <cstdio>
#include <stack>
using namespace std;
using node = array<int, 3>;
inline int max(int a, int b, int c) {
return max(max(a, b), c);
}
struct Graph{
struct node{
int to, nxt;
} edge[2000010 << 1];
int eid, head[1000010];
inline void add(int u, int v){
edge[++eid] = {v, head[u]};
head[u] = eid;
}
inline node & operator [] (const int x){
return edge[x];
}
} xym;
int n, m, val[1000010], ans;
vector<int> yzh[1000010];
int dfn[1000010], low[1000010], timer;
stack<int> st;
int sccno[1000010], scc_cnt;
void tarjan(int now, int fr){
dfn[now] = low[now] = ++timer, st.push(now);
for (int i = xym.head[now], to; to = xym[i].to, i; i = xym[i].nxt) {
if (!dfn[to]) tarjan(to, now), low[now] = min(low[now], low[to]);
else if (to != fr) low[now] = min(low[now], dfn[to]);
}
if (low[now] >= dfn[now]){
++scc_cnt;
while (true) {
int tp = st.top(); st.pop();
yzh[scc_cnt].push_back(tp), sccno[tp] = scc_cnt;
if (tp == now) break;
}
}
}
void move_to_end(vector<int> &vec, int x) {
vector<int> res;
int len = vec.size(), pos = 0;
for (int i = 0; i < len; ++i) {
if (vec[i] == x) {
pos = i;
break;
}
}
for (int i = pos + 1; i < len; ++i)
res.push_back(vec[i]);
for (int i = 0; i < pos; ++i)
res.push_back(vec[i]);
res.push_back(x);
vec = move(res);
}
bool vis[1000010];
node dfs(int now, int top, int fa) {
vis[now] = true;
move_to_end(yzh[now], top); // 把 top 放在最后
int tot = yzh[now].size();
vector<node> f(tot, {0, 0, 0}), dp(tot, {0, 0, 0});
for (int i = 0; i < tot; ++i) {
int son = yzh[now][i];
f[i][0] = 0, f[i][1] = val[son], f[i][2] = 0;
int mx = -0x3f3f3f3f;
for (int j = xym.head[son], to; to = xym[j].to, j; j = xym[j].nxt) {
int bl = sccno[to];
if (bl == fa || bl == now) continue;
node yzh = dfs(bl, to, now);
f[i][0] += max(yzh[0], yzh[2]);
f[i][1] += yzh[0];
f[i][2] += max(yzh[0], yzh[2]);
mx = max(mx, yzh[1] - max(yzh[0], yzh[2]));
}
f[i][2] += mx;
}
node res = {-0x3f3f3f3f, -0x3f3f3f3f, -0x3f3f3f3f};
yzh[now].clear();
yzh[now].shrink_to_fit();
if (tot == 1) {
res[0] = f[0][0];
res[1] = f[0][1];
res[2] = f[0][2];
return res;
}
// 左边是空的 并且可能被 1 占用
dp[0][0] = f[0][0], dp[0][1] = -0x3f3f3f3f, dp[0][2] = -0x3f3f3f3f;
for (int i = 1; i < tot; ++i) {
dp[i][0] = f[i][0] + max(dp[i - 1][0], dp[i - 1][2]);
dp[i][1] = f[i][1] + dp[i - 1][0];
dp[i][2] = max(dp[i - 1][1] + f[i][0], max(dp[i - 1][0], dp[i - 1][2]) + f[i][2]);
}
res[0] = max(res[0], dp[tot - 1][0]);
res[2] = max(res[2], dp[tot - 1][2]);
// 左边是空的 并且不能被 1 占用
dp[0][0] = f[0][0], dp[0][1] = -0x3f3f3f3f, dp[0][2] = -0x3f3f3f3f;
for (int i = 1; i < tot; ++i) {
dp[i][0] = f[i][0] + max(dp[i - 1][0], dp[i - 1][2]);
if (i != 1) dp[i][1] = f[i][1] + dp[i - 1][0];
else dp[i][1] = -0x3f3f3f3f;
dp[i][2] = max(dp[i - 1][1] + f[i][0], max(dp[i - 1][0], dp[i - 1][2]) + f[i][2]);
}
res[0] = max(res[0], dp[tot - 1][0]);
res[1] = max(res[0], dp[tot - 1][1]);
res[2] = max(res[2], dp[tot - 1][2]);
// 左边在真的旁边
dp[0][0] = -0x3f3f3f3f, dp[0][1] = -0x3f3f3f3f, dp[0][2] = f[0][2];
for (int i = 1; i < tot; ++i) {
dp[i][0] = f[i][0] + max(dp[i - 1][0], dp[i - 1][2]);
dp[i][1] = f[i][1] + dp[i - 1][0];
dp[i][2] = max(dp[i - 1][1] + f[i][0], max(dp[i - 1][0], dp[i - 1][2]) + f[i][2]);
}
res[0] = max(res[0], dp[tot - 1][0]);
res[2] = max(res[2], dp[tot - 1][2]);
// 左边是真的
dp[0][0] = -0x3f3f3f3f, dp[0][1] = f[0][1], dp[0][2] = -0x3f3f3f3f;
for (int i = 1; i < tot; ++i) {
dp[i][0] = f[i][0] + max(dp[i - 1][0], dp[i - 1][2]);
dp[i][1] = f[i][1] + dp[i - 1][0];
dp[i][2] = max(dp[i - 1][1] + f[i][0], max(dp[i - 1][0], dp[i - 1][2]) + f[i][2]);
}
res[2] = max(res[2], dp[tot - 1][0]);
f.clear(), f.shrink_to_fit();
dp.clear(), dp.shrink_to_fit();
return res;
}
signed main() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(val[i]);
for (int i = 1, u, v; i <= m; ++i) {
read(u), read(v);
xym.add(u, v), xym.add(v, u);
}
for (int i = 1; i <= n; ++i) if (!dfn[i]) tarjan(i, 0);
for (int i = 1; i <= n; ++i)
if (!vis[sccno[i]]) {
node res = dfs(sccno[i], i, 0);
ans += max(res[0], res[1], res[2]);
}
printf("%d", ans);
return 0;
}
后记 & 反思
没有什么是分类讨论解决不了的。如果有,那是你没有讨论细致。
[SDOI2010] 城市规划 题解的更多相关文章
- luogu 2478 [SDOI2010]城市规划 仙人掌上dp.
LINK:城市规划 以前ls 让写的时候由于看不懂题目+以为在图中的环上dp非常困难所以放弃治疗了. 现在终于能把题目看懂了 泪目... 题目其实就是在说 给出一张图这个有一个非常好的性质 满足每个点 ...
- BZOJ3456:城市规划——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3456 求出n个点的简单(无重边无自环)无向连通图数目 模数很熟悉,先敲一个NTT. 然后通过推导式 ...
- 【BZOJ-1952】城市规划 [坑题] 仙人掌DP + 最大点权独立集(改)
1952: [Sdoi2010]城市规划 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 73 Solved: 23[Submit][Status][ ...
- BZOJ-1951 古代猪文 (组合数取模Lucas+中国剩余定理+拓展欧几里得+快速幂)
数论神题了吧算是 1951: [Sdoi2010]古代猪文 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 1573 Solved: 650 [Submit ...
- 图论杂项细节梳理&模板(虚树,圆方树,仙人掌,欧拉路径,还有。。。)
orzYCB 虚树 %自为风月马前卒巨佬% 用于优化一类树形DP问题. 当状态转移只和树中的某些关键点有关的时候,我们把这些点和它们两两之间的LCA弄出来,以点的祖孙关系连成一棵新的树,这就是虚树. ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- 【题解】古代猪文 [SDOI2010] [BZOJ1951] [P2480]
[题解]古代猪文 [SDOI2010] [BZOJ1951] [P2480] 在那山的那边海的那边有一群小肥猪.他们活泼又聪明,他们调皮又灵敏.他们自由自在生活在那绿色的大草坪,他们善良勇敢相互都关心 ...
- 【题解】SDOI2010所驼门王的宝藏(强连通分量+优化建图)
[题解]SDOI2010所驼门王的宝藏(强连通分量+优化建图) 最开始我想写线段树优化建图的说,数据结构学傻了233 虽然矩阵很大,但是没什么用,真正有用的是那些关键点 考虑关键点的类型: 横走型 竖 ...
- 【题解】P4841 城市规划(指数型母函数+多项式Ln)
[题解]P4841 城市规划 P4841 城市规划 超级弱化版本(DP):POJ - 1737 两张图不同当且仅当边的分布不一样的时候,带编号最后乘一个阶乘即可,现在最主要的问题就是"联通& ...
- 【题解】P2480 [SDOI2010]古代猪文 - 卢卡斯定理 - 中国剩余定理
P2480 [SDOI2010]古代猪文 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 猪王国的文明源远流长,博大精 ...
随机推荐
- java8 API 函数式接口
简介 14年,Oracle公司如期发布了Java 8正式版,Java8提供了强大的流式处理及函数式接口编程 函数式接口编程,相信很多人在javascript中都使用过,比如回调函数,如今Java8也吸 ...
- 详解Kubernetes Pod优雅退出
1.概述 Pod优雅关闭是指在Kubernetes中,当Pod因为某种原因(如版本更新.资源不足.故障等)需要被终止时,Kubernetes不会立即强制关闭Pod,而是首先尝试以一种"优雅& ...
- injectionIII iOS代码注入工具(下)
injectionIII iOS代码注入工具(下) 本文将解决如何使用injectionIII对主页热重载,如果对injectionIII不了解的同学请回到上篇查看 Vaccine 简单地说Vacci ...
- Linux 内核:initcall机制与module_init
Linux 内核:initcall机制与module_init 背景 在做分享的时候,被资深的同事问起关于驱动加载时机与probe的问题.发现自己并不熟悉,因此学习了解一下. 学习本文你就可以知道,内 ...
- 关于Android开机动画调试的一点小小的经验
格式要求: 开机动画图片既可以是jpg文件,也可以是png文件,只是一定要按顺序命名.文件名命名的国际惯例是五位数,即:00000.00001. 00002--也可以是文件夹名称+下划线+序数,即:p ...
- k8s实战 ---- pod 基础
如果你对k8s还不了解,可以看下前文 k8s 实战 1 ---- 初识 (https://www.cnblogs.com/jilodream/p/18245222) 什么是pod,pod在 ...
- ubuntu20 python3 安装 easysnmp
前言 最近在 ubuntu20 上通过 pip3 安装 easysnmp,安装失败,系统提示:net-snmp/net-snmp-config.h: No such file or directory ...
- redis基本数据结构-字符串
reids字符串数据结构相关命令 序号 命令 命令实例 意义 1 set key value set bar 1 设置key为bar的值为"1" 2 incr k ...
- Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文
title: Nuxt 使用指南:掌握 useNuxtApp 和运行时上下文 date: 2024/7/21 updated: 2024/7/21 author: cmdragon excerpt: ...
- Nuxt.js 环境变量配置与使用
title: Nuxt.js 环境变量配置与使用 date: 2024/7/25 updated: 2024/7/25 author: cmdragon excerpt: 摘要:"该文探讨了 ...