【C++贪心】P8411 「SvR-1」Problem|普及
本文涉及知识点
C++贪心
「SvR-1」Problem
题目背景
小 L 打颓被 nodgd 发现,于是他开始做题了。
题目描述
他的 DS 非常菜,于是他把一共 n n n 道 DS 题加到了自己的计划题单里,其中第 i i i 道题的有趣程度为 a i a_i ai。
由于他并不精通 DS,他发现他在做一些题目之前需要先做另一些题目。这样的关系共有 n − 1 n - 1 n−1 组,他还发现每道题都出现在了这些关系中且没有重复。
他发现 ∀ 2 ≤ i ≤ n \forall 2 \leq i \leq n ∀2≤i≤n,第 i i i 题和第 f a i fa_i fai 题间存在上文所述的关系,且 1 ≤ f a i < i 1 \leq fa_i < i 1≤fai<i。他必须先做第 f a i fa_i fai 题后才能做第 i i i 题。
他发现,如果他在做一道题之前高兴程度为 k k k,则他做完第 i i i 题后,他的高兴程度便会变为 min ( k , a i ) \min(k, a_i) min(k,ai)。他做题前的高兴程度为无穷大。
他想问你在必须先做第 1 1 1 题且不能重复做某一道题的情况下,他在做题的全过程中每做完一道题后高兴程度之和的最大值。
输入格式
第一行,两个整数 n , s e e d n, seed n,seed;
由于输入量较大,我们采用如下方式生成 a i , f a i a_i, fa_i ai,fai。
C++:
typedef unsigned int uint;inline uint get_next(uint &seed){seed ^= seed << 13;seed ^= seed >> 17;seed ^= seed << 5;return seed;
}int main(){// ...for (int i = 1; i <= n; i++){a[i] = get_next(seed);}for (int i = 2; i <= n; i++){fa[i] = get_next(seed) % (i - 1) + 1;}// ...return 0;
}
使用其他语言的选手请参考「说明/提示」中的「伪代码参考」。
输出格式
一行,一个整数,表示所求的值。
样例 #1
样例输入 #1
6 114514
样例输出 #1
14907285111
提示
样例 #1 解释
在该组样例中 a = [ 3398922311 , 3077554952 , 2933028207 , 4018360144 , 1263042788 , 835814542 ] a = [3398922311, 3077554952, 2933028207, 4018360144, 1263042788, 835814542] a=[3398922311,3077554952,2933028207,4018360144,1263042788,835814542], f a 2 = f a 3 = f a 4 = 1 fa_2 = fa_3 = fa_4 = 1 fa2=fa3=fa4=1, f a 5 = f a 6 = 2 fa_5 = fa_6 = 2 fa5=fa6=2。
最优方案之一:依次做第 1 , 4 , 2 , 3 , 5 , 6 1, 4, 2, 3, 5, 6 1,4,2,3,5,6 题,最大值为 3398922311 + 3398922311 + 3077554952 + 2933028207 + 1263042788 + 835814542 = 14907285111 3398922311 + 3398922311 + 3077554952 + 2933028207 + 1263042788 + 835814542 = 14907285111 3398922311+3398922311+3077554952+2933028207+1263042788+835814542=14907285111。
伪代码参考
KaTeX parse error: Expected a control sequence at position 6: \def{̲\b}#1{ \textbf{…
其中 left ( x , d ) \text{left}(x,d) left(x,d) 和 right ( x , d ) \text{right}(x,d) right(x,d) 分别表示将 x x x 左移或右移 d d d 位。
数据规模与约定
本题自动开启捆绑测试和 O2 优化。
Subtask n ≤ 分值 1 10 10 2 1 0 4 20 3 1 0 6 20 4 无特殊限制 50 \newcommand{\arraystretch}{1.5} \begin{array}{c|c|c}\hline\hline \textbf{Subtask} & \bm{n \leq} & \textbf{分值} \\\hline \textsf{1} & 10 & 10 \\\hline \textsf{2} & 10^4 & 20 \\\hline \textsf{3} & 10^6 & 20 \\\hline \textsf{4} & \text{无特殊限制} & 50 \\\hline\hline \end{array} Subtask1234n≤10104106无特殊限制分值10202050
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 7 1 \leq n \leq 10^7 1≤n≤107, 0 ≤ s e e d < 2 32 0 \leq seed < 2^{32} 0≤seed<232。
贪心之临项交换
某步可以选择ai,或aj。选择大的。更新这两项目,对之前和之后的项目,无影响。即相邻两项ai,aj。以下两种情况不会同时存在:一,ai不是aj的前置。二,ai < aj。
否则更换之:
先选择ai,则这两项之和是:min(k,ai)+min(k,ai,aj)
先选择aj,则这两项之和是:min(k,aj)+min(k,ai,aj)
故先选择ai劣于先选择aj。
fa转为后序节点。每次都选择最大。
用优先队列时间复杂度是: O(nlogn) 第四个子测试过不了。
代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>#include <bitset>
using namespace std;template<class T = int>
vector<T> Read(int n,const char* pFormat = "%d") {vector<T> ret;T d ;while (n--) {scanf(pFormat, &d);ret.emplace_back(d);}return ret;
}template<class T = int>
vector<T> Read( const char* pFormat = "%d") {int n;scanf("%d", &n);vector<T> ret;T d;while (n--) {scanf(pFormat, &d);ret.emplace_back(d);}return ret;
}string ReadChar(int n) {string str;char ch;while (n--) {do{scanf("%c", &ch);} while (('\n' == ch));str += ch;}return str;
}typedef unsigned int uint;class Solution {
public:long long MaxS(const int N, uint seed) {vector<uint> a(N + 1);vector<vector<int>> vNext(N + 1);// ...for (int i = 1; i <= N; i++) {a[i] = get_next(seed);}for (int i = 2; i <= N; i++) {const int par = get_next(seed) % (i - 1) + 1;vNext[par].emplace_back(i);}priority_queue<pair<uint, int>> heap;heap.emplace(a[1], 1);long long ans = 0;uint cur = UINT_MAX;while (heap.size()) {const auto [curv, i] = heap.top();heap.pop();cur = min(cur, curv);ans += cur;for (const auto& next : vNext[i]) {heap.emplace(a[next], next);}}return ans;}inline uint get_next(uint& seed) {seed ^= seed << 13;seed ^= seed >> 17;seed ^= seed << 5;return seed;}};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUGint n;uint seed;scanf("%d%u", &n,&seed);auto res = Solution().MaxS(n,seed);cout << res << std::endl;return 0;
}
本题
父节点 一定 < 子节点。
所以从小到大处理节点时,所有的祖先节点一定已经处理,故:a[i] = min(a[父节点],a[i])
处理过程:选择最大值,相同值按父子关系选取。
然后次大值…
答案即是 ∑ \sum ∑a。
代码
核心代码
class Solution {public:long long MaxS(const int N, uint seed) {vector<uint> a(N + 1);vector<vector<int>> vNext(N + 1);// ...for (int i = 1; i <= N; i++) {a[i] = get_next(seed);}for (int i = 2; i <= N; i++) {const int par = get_next(seed) % (i - 1) + 1;a[i] = min(a[i], a[par]);} return accumulate(a.begin(),a.end(),0ll);}inline uint get_next(uint& seed) {seed ^= seed << 13;seed ^= seed >> 17;seed ^= seed << 5;return seed;}};
单元测试
TEST_METHOD(TestMethod11){ auto res = Solution().MaxS(6 ,114514);AssertEx(14907285111ll, res);}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。