Codeforces Round #699 (Div. 2) F - AB Tree(贪心、树上DP)超级清晰,良心题解,看不懂来打我 ~
整理的算法模板合集: ACM模板
点我看算法全家桶系列!!!
实际上是一个全新的精炼模板整合计划
Codeforces Round #699 (Div. 2) F - AB Tree
Problem F - AB Tree
输入 nnn, xxx,给你一棵 nnn 个结点根为 111 的树,请你为每一个结点分配一个字符 a
或者 b
。使得字符 a
的数量为 xxx 字符 b
的数量为 n−xn-xn−x 。
定义结点 vvv 上的字符串:
- 若结点 vvv 是根结点,则结点 vvv 的上的字符串为你为根结点分配的字符
- 否则,结点 vvv 上的字符串就是父结点上的字符串的末尾加上 结点 vvv 上面分配的字符。
请你为每个结点上分配字符,满足所有结点上的字符串的种类最少。
Solution
看上去又是一个贪心 + DP(没完了是吧 )
还是先分析一下题目有什么性质,比如这个字符串是什么,很明显,就是从每个结点的最上层的祖先开始(实际上就是根结点)一直到该结点组成的简单路径上经过的每个结点组成的字符串。我们发现字符串的长度也就是路径的长度,那么最长也就是树上最长的简单路径的长度 —— 树的直径 dist\tt distdist。最长的的字符串的长度也就是直径 dist\tt distdist + 1\tt 11 。也就是所有结点上的字符串的长度不会超过 dist\tt distdist + 1\tt 11 ,也就意味着所有的字符串最多有 dist\tt distdist + 1\tt 11 层。很明显,上层,也就是离根结点近的结点,出现在字符串里的次数最多,是好多字符串的基础(前排),所以我们可以想到,前面的尽量每一层(每一个字符串的前缀)都相同,这样最后总的字符串的种数会更小。
然后我们这里的直径 dist\tt distdist 严格意义上不能说是树的直径,因为字符串是从根结点开始的,所以应该是树的深度,也就是根结点的深度为 111 开始往下递推就行了。
因为我们只能也必须分配 xxx 个字符 a
,和 n−xn-xn−x 个字符 b
,所以我们很直观的一个贪心策略就是让上层尽可能的一致,直到某一个字符不够用为止。
我们设树的深度为 mmm 。
这样我们一共会有 mmm 层。
我们发现实际上每一层,都是对应的字符串的同样的位置,也可以说是同样的下标,也就是如果我们能保证每一层分配的字符相同,那么所有的字符串都会相同,也就是不同的字符串的个数就是不同长度的字符串的个数,也就是层数 mmm 。
我画一个图来帮助理解:
1a/ \2b 3b/ \ / \4a 5a 6a 7a
这道题一画图就太形象了,显然,上面的策略的是正确的
所以我们尽量去根据这个贪心策略去走。
所以我们这道题就变成了,从 mmm 层里,选择若干层,(设第 iii 层的结点个数为 numinum_inumi ),选择的这些层的结点个数之和等于 xxx ,也就是 a
的个数,然后剩下的自然都是 b
,也就能保证每一层的字符都相同,也就完成了上面的贪心策略。这很显然就是一个背包问题。我们预处理出来 numinum_inumi ,当作背包处理即可。
然后再来分析一下有没有其他的情况:即如果这种贪心策略不能满足,那么最小的答案是啥嘞 ~
我们发现,如果贪心策略不能满足,也就是差点,那么最多也就只会出现一层的结点字符不同,也就是需要被迫分配到两种字符。那么把这一层放到哪儿很关键。我们再来贪心。
我们知道原本贪心策略完美运行的时候,我们的答案是不同长度的字符串的个数,然后根据我们最开始的贪心策略,让不同的字符越低越好,也就是让不同的字符放到最低的叶子节点,对整体的答案影响最小,那么我们让需要被迫使用两种字符那一层,放到叶子节点,找到一个叶子节点最多的那一层,丢进去,损失最小。然后因为我们实际上不同字符只有 a
和 b
两种,也就是那一堆叶子节点,长度相同,末尾字符不同,但也只有两种情况,就是保证有儿子的都相同,不同的都填到叶子节点上,所以最后的答案在 mmm 的基础之上 + 111 ,也就是 m+1m+1m+1
总结一下
- 若背包可以实现选择 若干层,结点个数和为 xxx ,答案为 mmm,我们在DP的时候存一下选择方案,被装进背包里的层,为
a
,其余全部为b
- 若背包不能实现,我们就凑到最大的数,最后的答案为 m+1m+1m+1 ,我们找到叶子节点最多的那一层,把叶子节点上缺的,凑不够的
a
换成b
,剩下的就是我们当前能凑出来的最大的数,同上一种情况输出即可。
所以我们就dfs最大求一下深度以及每层的结点个数,叶子节点个数。
然后DP求凑成 xxx 的方案,若能凑成,就回溯一下找一下DP的选择方案,就是答案。要是凑不成就是第二种情况我们就找到能凑成的最大的数,同样先找DP方案,然后因为我们的 a
有剩余,所以我们找到一个叶子节点个数大于差值的那一层,填上 a
,然后就没了…
然后就没了…
然后,要是看不懂,你飞过来打我呀
然后我在官方题解的评论区里看到可以用bitset优化,就找了一位大佬的AC代码学习了一下,借鉴了他的DP部分,把我的垃圾背包改成bitset了,挺巧妙的 ~
呜呜呜
哦哦哦,树是无向边,但是如何判断是不是叶子节点呢?我用的是如果前向星索引数组 head = -1
,则没有子节点,但是因为最开始我建图连的是双向的… 所以没办法判断了。然后因为我们的dfs只需要向下走,所以我们只需要连单向边就行(这都能A10个点…以及这道题直接七十多个点,丧心病狂 )
wtcl
然后就A了…
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <bitset>
#include <vector>
#include <unordered_map>
using namespace std;const int N = 100007, M = 500007, INF = 0x3f3f3f3f;typedef long long ll;
typedef int itn;int n, m, t, x;
int fa[N];
int deep[N];
int maxx;
int num[N];//每一层结点个数
int cnt;
int lson[N];//叶子节点
int head[N], ver[M], edge[M], nex[M], tot;
bitset<N> f[2007];//能否凑齐 x,使用 bitset 优化
int val[N];
unordered_map<int, int> vis;
//int vis[N];
bool ok[N];
vector<int> v[N];void add(int x, itn y)
{ver[tot] = y;nex[tot] = head[x];head[x] = tot ++ ;
}void dfs1(int x)
{deep[x] = deep[fa[x]] + 1;//这里根结点 deep[1] = 1 所以不用再 +1 了num[deep[x]] ++ ;maxx = max(maxx, deep[x]);//树的直径,实际上也就是最大深度if (head[x] == -1) //没有子节点,说明是叶子结点lson[deep[x]] ++ ;//每层的叶子结点个数for (int i = head[x]; ~i; i = nex[i]) {int y = ver[i];dfs1(y);}
}void dfs2(itn x, int t)
{if(x == 0)return;for (int i = 0; i < (int)v[x].size(); ++ i) {if(val[x] > t || f[x - 1][t])break;t -= val[x], ok[v[x][i]] = true;}dfs2(x - 1, t);
}void init()
{memset(head, -1, sizeof head);tot = 0;
}void solve()
{init();scanf("%d%d", &n, &x);for (int i = 2; i <= n; ++ i) {scanf("%d", &fa[i]);//add(i, fa[i]);add(fa[i], i);}dfs1(1);//预处理每层结点个数//把抽象的树转化为一个个物品,存到 v 里,离散化一下,种类(结点个数)和个数(层数)//cout << "ok" << endl;//一共cnt种,for (int i = 1; i <= maxx; ++ i) {if (vis[num[i]]) {v[vis[num[i]]].push_back(i);}else {vis[num[i]] = ++ cnt;val[cnt] = num[i];v[cnt].push_back(i);//来找最后选了第几层}}f[0][0] = 1;for (int i = 1; i <= cnt; ++ i) {f[i] = f[i - 1];itn Size = v[i].size();for (int j = 1; j <= Size; j <<= 1) {Size -= j;f[i] |= (f[i] << (j * val[i]));}if(Size > 0)f[i] |= f[i] << (Size * val[i]);}if(f[cnt][x]) {printf("%d\n", maxx);dfs2(cnt, x);//还原dp的方案for (int i = 1; i <= n; ++ i) {putchar(ok[deep[i]] ? 'a' : 'b');}}//凑不齐,'a' 有剩余else {int res = INF;for (int i = x; i >= 0; -- i) {if(f[cnt][i]) {res = i;//找到能凑到的最大的数break;}}dfs2(cnt, res);int pos = -1;for (int i = 1; i <= maxx; ++ i){if(!ok[i] && lson[i] >= x - res) {pos = i;break;}}printf("%d\n", maxx + 1);for (int i = 1; i <= n; ++ i) {if(deep[i] == pos && head[i] == -1) {if(res == x) {putchar('b');}elseputchar('a'), ++ res;}else {putchar(ok[deep[i]] ? 'a' : 'b');}}}return ;
}itn main()
{solve();return 0;
}
Codeforces Round #699 (Div. 2) F - AB Tree(贪心、树上DP)超级清晰,良心题解,看不懂来打我 ~相关推荐
- Codeforces Round #595 (Div. 3) F. Maximum Weight Subset 树形dp
传送门 文章目录 题意: 思路: 题意: n≤200n\le200n≤200 思路: 明显的树形dpdpdp,所以考虑一下dpdpdp状态. 这个题状态挺神的..可能是因为我太菜了,看了半天才看懂. ...
- Codeforces Round #656 (Div. 3) F. Removing Leaves 贪心 + 模拟
传送门 文章目录 题意: 思路: 题意: 思路: 首先有一个贪心策略就是每次都找一个叶子节点最多的点,让后删掉他的kkk个叶子节点,现在我们就来考虑如何模拟这个过程. 我们整一个vector<s ...
- Codeforces Round #375 (Div. 2) F. st-Spanning Tree 生成树
F. st-Spanning Tree 题目连接: http://codeforces.com/contest/723/problem/F Description You are given an u ...
- Codeforces Round #665 (Div. 2) Maximum Distributed Tree(树上贪心)
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 CF1401D Maximum Distributed Tree(树上贪心) 给定一棵 nnn 个节点 ...
- CodeCraft-22 and Codeforces Round #795 (Div. 2) F. K-Set Tree
题目链接 题意 给定一个树,f(r,S)f(r,S)f(r,S) 表示该树以 rrr 为根,节点 VVV 的子集 ∣S∣=k|S|=k∣S∣=k,包含 SSS 的最小子树的大小.求所有 f(r,S)f ...
- Codeforces Round #694 (Div. 2) F. Strange Housing (贪心思维)
F. Strange Housing 题意 有 nnn 个点和 mmm 条边,对点进行染色.要求一条边的两个点不能都染色,并且删除两端都没有染色的边之后,图连通.请给出一种染色方案. 题解 暴力贪心即 ...
- Codeforces Round #521 (Div. 3): F. Pictures with Kittens(DP+单调队列)
题意: 你有n幅画,第i幅画的好看程度为ai,再给你两个数字k,x,表示你要从中选出刚好x幅画,并且相邻两幅画的距离不能≥k,好看程度之和最大能多少,选不出来输出-1,F1数据范围<200,F2 ...
- Codeforces Round #699 (Div. 2) (A ~ F)6题全,超高质量良心题解【每日亿题】2021/2/6
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 Codeforces Round #699 (Div. 2) (A.B.C)[每日亿题]2021/2/ ...
- Codeforces Round #644 (Div. 3) F.Spy-string
Codeforces Round #644 (Div. 3) F.Spy-string 题目链接 You are given n strings a1,a2,-,an: all of them hav ...
最新文章
- 常用深度学习框——Caffe/TensorFlow / Keras/ PyTorch/MXNet
- Python修改Mitsuba的XML相关参数
- myeclipse设置
- 数据结构1_java---单链表的操作,约瑟夫问题
- PyQt:如何给界面自定义背景?
- redisson MultiLock原理及分布式锁的应用
- java jxl之Excel的读取
- qt开发环境 - c++之无名名字空间,名字空间嵌套邻近原则(内藏外),名字空间别名
- C#题目及答案(1)
- Python版归并排序算法(附Python程序__name__属性用法演示视频)
- 入门:HTML表单与Java 后台交互(复选框提交)
- c#asp.net url 传递中文参数要使用 System.Web.HttpUtility.UrlEncode 而不能使用Server.UrlEncode...
- CCSP2020比赛太原理工学子再创佳绩
- matlab图片集成成视频
- 宝峰c1对讲机写频软件_宝峰对讲机写频软件
- EGM2008大地水准面模型在工程中的应用综述
- 为什么要了解和使用拉姆达——走进Java Lambda(〇)
- 计算机大赛鼓励语录,比赛鼓励的话
- Python中的shuffle()函数
- Linux——权限|shell运行原理——外壳程序|Linux权限的概念|对人操作|角色和文件操作|文件类型访问权限|修改权限ugo+-|8进制|修改权限|更改文件的拥有