「codeforces - 1481F」AB Tree
2022/2/4 23:49:18
本文主要是介绍「codeforces - 1481F」AB Tree,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
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的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享