「codeforces - 1481F」AB Tree
理一下逻辑,主要讲一下我做题时的疑惑和其它题解没提到的细节。
首先容易看到,一个必然不劣的贪心策略是把尽量靠近根的层铺成同样的字符。也许会有疑惑,字符串是否本质不同的判定每个位置地位相等。然而在这题里面字符串个数的贡献是和结点所为根的子树大小有关的,所以这个贪心不劣。
设树的最大深度为 \(d\),那么如果我们能实现每层都铺成同样的字符,答案就是不同长度的字符串个数,也就是 \(d\)。
考虑构造出答案为 \(d+1\) 的情况,这时候贪心就跑不出来了,具体情况是,会出现一层既有 a 又有 b,其他层都是 pure a 或 pure b。
主要问题就在于 miscellaneous level 放在哪里,才能让答案最小。直觉地,我们认为把它放在尽量低(离根更远)的 level 会更优。但是其实贡献结束的地方是叶子,所以其实我们应该把它放在叶子尽量多的 level。这个时候答案为 \(d+1\)。
根据以上的推理,我们得出一个论断:最优答案在 \(d\) 和 \(d+1\) 出现。剩下的问题就是如果判断是前者还是后者。
其实这是一个简单可达性 dp 可以解决的问题,将每一个 level 压成一个 item,其体积为当层的结点个数。但是这个是 \(O(n^2)\) 的,要考虑优化。有两种解决的道路,分析后可以发现一个根号规模的结论,或者用 std::bitset 优化 dp。
注意 std::bitset 优化 dp 后状态略有不同……
#include<bits/stdc++.h>
#define cmin(x, y) x = std::min(x, y)
#define cmax(x, y) x = std::max(x, y)
#define fors(i, l, r, ...) for(int i = (l), REP##i = (r), ##__VA_ARGS__; i <= REP##i; ++i)
#define dfors(i, r, l, ...) for(int i = (r), REP##i = (l), ##__VA_ARGS__; i >= REP##i; --i)
// observation: amount different answers would be 2: maximum depth, or, it plus 1
int n, _x, dep[100100], nod[100100], leaf[100100], len, vol[100100];
// params: @nod[i]: amout of nodes at level i; @leaf[i]: amout of leaves at lv i;
// #vol[i]: volume of i-th item
std::bitset<100100> f[2100]; // if can assign it to j perfectly within lefmost i items
std::vector<int> adj[100100], zip[100100];
void pre_dfs(const int x) {
nod[dep[x]]++,leaf[dep[x]] += adj[x].empty();
for(const int y : adj[x]) dep[y] = dep[x]+1,pre_dfs(y);
}
int pre_zip() {
static int vis[100100], tot;
fors(i, 1, len) {
if(!vis[nod[i]]) vol[vis[nod[i]] = ++tot] = nod[i];
zip[vis[nod[i]]].emplace_back(i);
}
return tot; // amount of items
}
signed main() {
std::ios_base::sync_with_stdio(0);
std::cin.tie(0);
std::cin >> n >> _x;
fors(i, 1, n-1, f) std::cin >> f,adj[f].emplace_back(i+1);
pre_dfs(dep[1] = 1),len = *std::max_element(dep+1, dep+n+1); // processing basic information
int m = pre_zip(); // compressing levels with same amout of nodes
fors(i, f[0][0] = 1, m, tmp) {
f[i] = f[i-1],tmp = zip[i].size();
for(int j = 1; j <= tmp; j *= 2) tmp -= j,f[i] |= (f[i]<<(vol[i]*j));
if(tmp > 0) f[i] |= (f[i]<<(vol[i]*tmp));
}
std::vector<bool> k(len);
std::function<void(int, int)> find_path = [&](const int rem, int t) { // reviving way we DP through
if(rem == 0) return;
for(const int x : zip[rem]) {
if(vol[rem] > t || f[rem-1][t]) break;
t -= vol[rem],k[x-1] = 1;
}
find_path(rem-1, t);
};
if(f[m][_x]) { // when greedy strategy runs well
std::cout << len << "\n";
find_path(m, _x);
fors(i, 1, n) std::cout << (k[dep[i]-1]?'a':'b');
return std::cout << "\n", 0;
}
std::cout << len+1 << "\n"; // otherwise the answer would be maximum depth plus 1
int tmp = std::numeric_limits<int>::max(), tmp1 = -1;
dfors(i, _x, 0) if(f[m][i]) {
tmp = i; break;
}
find_path(m, tmp);
fors(i, 1, len) if(!k[i-1] && leaf[i] >= _x-tmp) {
tmp1 = i; break;
}
fors(i, 1, n) {
if(dep[i] == tmp1 && adj[i].empty()) std::cout << (tmp == _x?'b':(++tmp, 'a'));
else std::cout << (k[dep[i]-1]?'a':'b');
}
return std::cout << "\n", 0;
}
「codeforces - 1481F」AB Tree的更多相关文章
- 「算法笔记」Link-Cut Tree
一.简介 Link-Cut Tree (简称 LCT) 是一种用来维护动态森林连通性的数据结构,适用于动态树问题. 类比树剖,树剖是通过静态地把一棵树剖成若干条链然后用一种支持区间操作的数据结构维护, ...
- 「CodeForces 581D」Three Logos
BUPT 2017 Summer Training (for 16) #3A 题意 给你三个矩形,需要不重叠不留空地组成一个正方形.不存在输出-1,否则输出边长和这个正方形(A,B,C表示三个不同矩形 ...
- 「CodeForces - 50C 」Happy Farm 5 (几何)
BUPT 2017 summer training (16) #2B 题意 有一些二维直角坐标系上的整数坐标的点,找出严格包含这些点的只能八个方向走出来步数最少的路径,输出最少步数. 题解 这题要求严 ...
- 「CodeForces - 598B」Queries on a String
BUPT 2017 summer training (for 16) #1I 题意 字符串s(1 ≤ |s| ≤ 10 000),有m(1 ≤ m ≤ 300)次操作,每次给l,r,k,代表将r位置插 ...
- 「CodeForces - 717E」Paint it really, really dark gray (dfs)
BUPT 2017 summer training (for 16) #1H 题意 每个节点是黑色or白色,经过一个节点就会改变它的颜色,一开始在1节点.求一条路径使得所有点变成黑色. 题解 dfs时 ...
- 「CodeForces 476A」Dreamoon and Stairs
Dreamoon and Stairs 题意翻译 题面 DM小朋友想要上一个有 \(n\) 级台阶的楼梯.他每一步可以上 \(1\) 或 \(2\) 级台阶.假设他走上这个台阶一共用了 \(x\) 步 ...
- 「CodeForces 546B」Soldier and Badges 解题报告
CF546B Soldier and Badges 题意翻译 给 n 个数,每次操作可以将一个数 +1,要使这 n 个数都不相同, 求最少要加多少? \(1 \le n \le 3000\) 感谢@凉 ...
- 「Codeforces 79D」Password
Description 有一个 01 序列 \(a_1,a_2,\cdots,a_n\),初始时全为 \(0\). 给定 \(m\) 个长度,分别为 \(l_1\sim l_m\). 每次可以选择一个 ...
- 「Codeforces 468C」Hack it!
Description 定义 \(f(x)\) 表示 \(x\) 的各个数位之和.现在要求 \(\sum_{i=l}^rf(i)\bmod a\). 显然 ans=solve(l,r)%a; if(a ...
- 「Codeforces 724F」Uniformly Branched Trees
题目大意 如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的. 将中间节点定义为度数大于 \(1\) 的节点.计算由 \(n\) 个节点,其中所有的中间节点度数都为 \(d\) 的互不同构的树 ...
随机推荐
- 图灵丛书——GitHub入门
这是一篇关于我个人学习 GitHub 的笔记,主要是记录一些我认为比较重要的知识点,以及一些我认为比较好的学习资料. 学习书籍:GitHub 入门与实践(图灵程序设计丛书) 这本书的目录是这样的 第 ...
- JAVA SE基础《一》----JAVA入门
初识Java 1.Java背景知识 java是美国sun公司(Stanford University Network)在1995年推出的一门计算机高级编程语言. Java早期称为Oak(橡树),后期改 ...
- 关于linq Where中的”或者“运算只查询出来满足一种条件的数据的问题,本质是IEnumerable和IQuerable之间的区别
如下代码所示,其中的"query"返回值类型为IQuerable var query = _deviceRepository.GetAll().AsNoTracking() .Wh ...
- 从AWS中学习如何使用AmazonDynamoDB存储卷
目录 <35. <从 AWS 中学习如何使用 Amazon DynamoDB 存储卷>>:从 AWS 中学习如何使用 Amazon DynamoDB 存储卷 随着云计算技术的迅 ...
- JAVA获取字符串内的括号对;获取括号对的内容;按指定规则返回括号对位置;
先看结果:处理字符串 "这个是一条测试用的字符串[ ( 5 ( 4( 3 [(1) (2)] ))(7))][(6)]" 结果 解决思路:参考正则表达式里面出入站部分 代码实现如下 ...
- Java 新的选择,Solon v2.3.7 发布
Solon 是什么开源项目? 一个,Java 新的生态型应用开发框架.它从零开始构建,有自己的标准规范与开放生态(历时五年,已有全球第二级别的生态规模).与其他框架相比,它解决了两个重要的痛点:启动慢 ...
- Spring Cloud 之OpenFeign
Spring Cloud 之OpenFeign 一:简介 Feign是一个声明式(对比RestTemplate编程式)的服务客户端,即通过@FeignClient注解即可声明一个接口(interf ...
- 从 iOS App 启动速度看如何为基础性能保驾护航
1 前言 启动是App给用户的第一印象,一款App的启动速度,不单单是用户体验的事情,往往还决定了它能否获取更多的用户.所以到了一定阶段App的启动优化是必须要做的事情.App启动基本分为以下两种 1 ...
- Mysql高级3-索引的结构和分类
一.索引概述 1.1 索引的介绍 索引index:是帮助 Mysql 高效获取数据 的 有序的数据结构,在数据之外,数据库系统维护着的满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据 ...
- 学习狂神Spring5_课堂笔记(更新中)
Spring 简介 spring,英文单词为春天,表示为软件行业带来了春天. 2002年,首次推出了Spring框架的雏形:interface21框架.官网为:www.interface21.io但是 ...