link。

理一下逻辑,主要讲一下我做题时的疑惑和其它题解没提到的细节。

首先容易看到,一个必然不劣的贪心策略是把尽量靠近根的层铺成同样的字符。也许会有疑惑,字符串是否本质不同的判定每个位置地位相等。然而在这题里面字符串个数的贡献是和结点所为根的子树大小有关的,所以这个贪心不劣。

设树的最大深度为 \(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的更多相关文章

  1. 「算法笔记」Link-Cut Tree

    一.简介 Link-Cut Tree (简称 LCT) 是一种用来维护动态森林连通性的数据结构,适用于动态树问题. 类比树剖,树剖是通过静态地把一棵树剖成若干条链然后用一种支持区间操作的数据结构维护, ...

  2. 「CodeForces 581D」Three Logos

    BUPT 2017 Summer Training (for 16) #3A 题意 给你三个矩形,需要不重叠不留空地组成一个正方形.不存在输出-1,否则输出边长和这个正方形(A,B,C表示三个不同矩形 ...

  3. 「CodeForces - 50C 」Happy Farm 5 (几何)

    BUPT 2017 summer training (16) #2B 题意 有一些二维直角坐标系上的整数坐标的点,找出严格包含这些点的只能八个方向走出来步数最少的路径,输出最少步数. 题解 这题要求严 ...

  4. 「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位置插 ...

  5. 「CodeForces - 717E」Paint it really, really dark gray (dfs)

    BUPT 2017 summer training (for 16) #1H 题意 每个节点是黑色or白色,经过一个节点就会改变它的颜色,一开始在1节点.求一条路径使得所有点变成黑色. 题解 dfs时 ...

  6. 「CodeForces 476A」Dreamoon and Stairs

    Dreamoon and Stairs 题意翻译 题面 DM小朋友想要上一个有 \(n\) 级台阶的楼梯.他每一步可以上 \(1\) 或 \(2\) 级台阶.假设他走上这个台阶一共用了 \(x\) 步 ...

  7. 「CodeForces 546B」Soldier and Badges 解题报告

    CF546B Soldier and Badges 题意翻译 给 n 个数,每次操作可以将一个数 +1,要使这 n 个数都不相同, 求最少要加多少? \(1 \le n \le 3000\) 感谢@凉 ...

  8. 「Codeforces 79D」Password

    Description 有一个 01 序列 \(a_1,a_2,\cdots,a_n\),初始时全为 \(0\). 给定 \(m\) 个长度,分别为 \(l_1\sim l_m\). 每次可以选择一个 ...

  9. 「Codeforces 468C」Hack it!

    Description 定义 \(f(x)\) 表示 \(x\) 的各个数位之和.现在要求 \(\sum_{i=l}^rf(i)\bmod a\). 显然 ans=solve(l,r)%a; if(a ...

  10. 「Codeforces 724F」Uniformly Branched Trees

    题目大意 如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的. 将中间节点定义为度数大于 \(1\) 的节点.计算由 \(n\) 个节点,其中所有的中间节点度数都为 \(d\) 的互不同构的树 ...

随机推荐

  1. HashMap 以及多线程基本感念

    接口 Map :映射项,(键值对 ) 的容器注意: 键 是唯一的 值 是可以重复的实现类 HashMap :哈希表结构 允许使用null值 和 null 键 线程不安全 键唯一 无序 linkedHa ...

  2. R 语言画图中英文字体解决方案

    在某些时候,需要在 R 画图中添加中文,但是默认情况下,R 对中文的支持不好.这里推荐一个showtext的 R 包.如果需要将含有中文字体的图形保存为 pdf 文件,可以使用下面讲到的方案,最新版的 ...

  3. ORM核心功能之导航属性- EFCore和 SqlSugar

    导航属性 导航属性是作为ORM核心功能中的核心,在SqlSugar没有支持导航属性前,都说只是一个高级DbHelper, 经过3年的SqlSugar重构已经拥有了一套 非常成熟的导航属性体系,本文不是 ...

  4. NixOS 与 Nix Flakes 新手入门

    独立博客阅读: https://thiscute.world/posts/nixos-and-flake-basics/ 长文警告️ 本文的目标 NixOS 版本为 22.11,Nix 版本为 2.1 ...

  5. CSS 图片加载提前占位 padding-top、padding-bottom

    今天聊一个图片加载提前占位的一个问题 ,内容比较适合初学者. 起因 在响应式页面当中,图片加载之前是不知道图片高度的,加载成功图片完全撑开.如果不做提前占位会把下面的内容挤下去,页面出现抖动,就像下面 ...

  6. 园子的商业化努力-行行AI人才培养「常青藤计划」

    各位园子的小伙伴: 感谢大家长期对园子的支持,AI大模型出现之后,各行各业都在积极思考如何应对,如何把业务场景和AI结合.在这个过程中,AI人才缺乏是最核心的问题. 基于此,园子打算在AI人才培养方面 ...

  7. 为什么要重写equals要重写hashcode方法

    Java 比较(==, equals) 一.= = ==:比较两个对象的引用是否是同一个地址 二.equals object中equals方法调用的就是==,可以在其他类中重写该方法. 三.为什么要重 ...

  8. 在linux上启动arthas报“Can not find java process”

    发生背景 完整报错信息: [***@localhost ~]$ java -jar arthas-boot.jar [INFO] JAVA_HOME: /usr/lib/jvm/java-1.8.0- ...

  9. webshell工具流量特征

    常见的webshell管理工具及流量特征 菜刀 作为老牌 Webshell 管理神器,中国菜刀的攻击流量特征明显,容易被各类安全设备检测,实际场景中越来越少使用,加密 Webshell 正变得日趋流行 ...

  10. Centos7中Jar快速启动脚本

    Centos7中Jar快速启动脚本 创建一个文本,将以下脚本内容复制到文本当中,重命名文本后缀为.sh 注意:根据自己的项目进行更改相关内容,对应注释已说明 #!/bin/sh APP_NAME=ma ...