【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 n1 组,他还发现每道题都出现在了这些关系中且没有重复。

他发现 ∀ 2 ≤ i ≤ n \forall 2 \leq i \leq n ∀2in,第 i i i 题和第 f a i fa_i fai 题间存在上文所述的关系,且 1 ≤ f a i < i 1 \leq fa_i < i 1fai<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} Subtask1234n10104106无特殊限制分值10202050

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 7 1 \leq n \leq 10^7 1n107 0 ≤ s e e d < 2 32 0 \leq seed < 2^{32} 0seed<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 _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	int 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++**实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件架构师何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值