首页 > 其他分享 >CF2029C New Rating

CF2029C New Rating

时间:2024-12-10 18:31:49浏览次数:10  
标签:Rating int DP CF2029C ans 跳跃 New now dp

思路(二分 + 数据结构优化DP)

大致题意为:一个值 \(x\) 初始为 \(0\),然后有一个数组 \(a\),遍历一次数组。
如果 \(a_i > x\),则 \(x + 1\)。
如果 \(a_i < x\),则 \(x - 1\)。
如果 \(a_i = x\),则 \(x\) 不变。
必须且只能跨越一段连续区间,求 \(x\) 的最大可能值。

后面的由前面的计算得到,并且前面的不受后面的影响,并且必须跳跃一段,很容易想到 DP。

首先是 DP 状态
最优解是开三个状态来 DP,这个官方题解讲得很清楚,因此这里提供一个我赛时写的两个状态的思路。
根据题目描述,可以连续也可以跳跃。
因此我选择给每个点 \(i\) 设两个状态:\(i\) 由前一个转移得到和由更前面的跳跃得到。
以下用 \(dp_{i, 0}\) 表示第 \(i\) 个点是由前面跳跃得到的,用 \(dp_{i, 1}\) 表示第 \(i\) 个点是由前一个点转移得到。

首先我们可以预处理一下一次也不跳的时候,每一个点的答案是多少,把它存入 \(dp_{i, 1}\) 中。这个很简单,按照题意从前往后模拟一遍即可。

然后我们考虑要跳跃的,也就是 \(dp_{i, 0}\) 的值。
因为题目要我们只能跳一次,所以,跳跃后的值只能通过没有跳过的值来转移,也就是 \(dp_{j, 1}\) 的值。

那么问题来了,只要和 \(i\) 有间隔的前面的点都可以作为转移的前置状态,选哪一个呢?
题目要我们求的是最后结果的最大值,不难发现,贪心地想,最后结果要最大,每一个局部的结果也要尽可能大。
因为这样的话,在 \(i\) 之后,经历相同的增增减减,在最后的答案一定会更大。
于是我们只有在这个点是增的时候才会更优,也就是在前面选择一个 \(dp_{i, 0}\) 的值比 \(a_i\) 小的点来转移。
简单证明:如果 \(dp_{j, 1} \geq a_i\),因为是从0增上来的,那么一定可以在 \(j\) 之前找到一个 \(k\),满足 \(dp_{k, 1} < a_i\),证毕。)
又因为结果要尽可能的大,所以要在更小的点中选一个最大的。
那么转移方程就出来了:\(dp_{i, 0} = \max(dp_{j, 1} + 1)\),\(j\) 满足 \(j < i - 1\) 并且 \(dp_{j, 1} < a_i\)。

不难发现,如果暴搜的话,这样的复杂度是 \(O(n ^ 2)\) 的。
那再细品一下这句话:“比 \(a_i\) 更小的点中挑一个最大的。”那不就可以二分了吗。
我们可以把 \(j \in [1, i - 2]\) 的 \(dp_{j, 1}\) 的值以及 \(0\) 压入一个便于二分数据结构中,比如 mapset
我赛时使用的树状数组(赛后发现 set 或者 map 就可以了而且复杂度更优)。
每次转移时,二分找到小于 \(a_i\) 中最大的一个值,用来转移得到 \(dp_{i, 0}\)。
这个过程可以在循环的过程中同时进行,每次处理完一个 \(i\) 的 \(dp_{i, 0}\) 后将 \(dp_{i - 1, 0}\) 压入数据结构即可。

到这里,我们就处理出了每一个点不跳跃到达和只跳跃一次到达能够得到的最大值。
要得到最后答案,我们还需要从前往后来一轮 DP。

这里我们再开一个 DP 数组 \(ans\)。
\(ans_{i, 1}\) 表示到 \(i\) 为止没有跳跃过得到的最大值。
\(ans_{i, 0}\) 表示到 \(i\) 为止跳跃过了到的最大值。

那么转移也很明显。
因为 \(dp_{i, 1}\) 预处理的就是到 \(i\) 为止没有跳跃过,所以 \(ans_{i, 1} = dp_{i, 1}\)。
而 \(ans_{i, 0}\) 因为表示的是到 \(i\) 为止跳跃过,所以我们只需要把在跳跃到 \(i\) 前面的位置和刚好跳跃得到 \(i\) 这个位置的结果取个最大值。
也就是假设跳跃到前面再不跳跃到 \(i\) 的答案设为 \(now\)。
如果 \(ans_{i - 1, 0} < a_i\),\(now = ans_{i - 1, 0} + 1\)。
如果 \(ans_{i - 1, 0} > a_i\),\(now = ans_{i - 1, 0} - 1\)。
如果 \(ans_{i - 1, 0} = a_i\),\(now = ans_{i - 1, 0}\)。
所以 \(ans_{i, 0} = \max(now, dp_{i, 0})\)。
这里要特别注意一下,如果 \(i = 1\),\(ans_{1, 0}\) 应设置为一个极小值,因为 \(1\) 这个点是不能通过跳跃得到的,这个状态不存在。

最后取答案的时候,其实就是 \(i \in [1, n - 1]\) 时,和 \(ans_{i, 1}\) 取最小,表示前面都不跳,后面全跳。\(i = n\) 时,和 \(ans_{n, 0}\) 取最小,表示在前面已经跳跃过了并且到达了 \(n\) 这个点。

时间复杂度 \(O(n \times \log{n})\)。
写题解的时候又想了一下,其实数据结构也可以不用,只需要记录到 \(i - 1\) 这个点时 \(dp_{j, 1}\) 的最大值就行了,因为是从 \(0\) 增长上来的,那么从 \(0\) 到 \(\max(dp_{j, 1})\) 之间的数一定都出现过,这样的话 \(O(n)\) 就可以了。

AC CODE

#include <bits/stdc++.h>
#define int long long
#define inf 2e18
#define ull unsigned long long
#define ls o << 1
#define rs o << 1 | 1

using namespace std;

const int N = 3e5 + 9;
int a[N];
int dp[N][2];
int ans[N][2];
int t[N];
int n;

int lowbit(int x){return x & -x;}

void add(int v, int x)
{
	for(int i = v;i <= n;i += lowbit(i))t[i] += x;
}

int query(int v)
{
	int res = 0;
	for(int i = v;i;i -= lowbit(i))res += t[i];
	return res;
}

void init()
{
	for(int i = 1;i <= n;i ++)t[i] = 0;
	for(int i = 1;i <= n;i ++)
	{
		dp[i][0] = dp[i][1] = 0;
		ans[i][0] = ans[i][1] = 0;
	}
}

void solve()
{
	cin >> n;
	
	init();
	
	for(int i = 1;i <= n;i ++)cin >> a[i];
	
	int now = 0;
	for(int i = 1;i <= n;i ++)//预处理不跳跃的情况
	{
		if(a[i] > now)dp[i][1] = ++ now;
		else if(a[i] == now)dp[i][1] = now;
		else if(a[i] < now)dp[i][1] = -- now;
	}
	
	add(0 + 1, 1);
	for(int i = 2;i <= n;i ++)//得到跳到i这个位置的dp值
	{
		int pre = query(a[i]);
		int l = 0, r = a[i] + 1;
		while(l + 1 != r)//二分找最优(用set或map更优)
		{
			int mid = (l + r) >> 1;
			if(query(mid) < pre)l = mid;
			else r = mid;
		}
		dp[i][0] = r;
		add(dp[i - 1][1] + 1, 1);//将i - 1的没跳跃的dp值压入数据结构
	}
	
	for(int i = 1;i <= n;i ++)//处理到每个点为止的最优答案
	{
		ans[i][1] = dp[i][1];
		
		if(i == 1)ans[i][0] = -inf;
		else
		{
			int now = ans[i - 1][0];
			if(now > a[i])now --;
			else if(now < a[i])now ++;
			
			ans[i][0] = max(now, dp[i][0]);
		}
	}
	
	int res = 0;
	for(int i = 1;i < n;i ++)res = max(res, ans[i][1]);//跳最后一段
	res = max(res, ans[n][0]);//跳前面的
	
	cout << res << '\n';
}

signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

    int t = 1;cin >> t;
    while(t --)solve();

    return 0;
}

标签:Rating,int,DP,CF2029C,ans,跳跃,New,now,dp
From: https://www.cnblogs.com/TianTianChaoFangDe/p/18597832

相关文章

  • 古早的遗传算法碰到LLM->AutoDAN Generating Stealthy Jailbreak Prompts onAligned L
    师兄推给我的一篇ICLR,抽出时间阅读整理了附录前的内容......
  • NewStar CTF 2024-week1-web
    headach3题目提示:head我们右键点查看,在网络处添加HEAD请求头就得到了flag:flag{You_Ar3_R3Ally_A_9ooD_d0ctor}会赢吗查看页面源代码,得到flag第一部分:ZmxhZ3tXQTB3继续访问/4cqu1siti0n查看页面源代码我们使用post方法请求接口,得到第二部分flag:IV95NF9yM2Fs我们继续......
  • 为什么 super().__new__(cls, name, bases, dct) 中的 cls 是显式传递的,而不是像 self
    问题来源:为什么定义元类和自定义元类时,在调用父类的__new__方法时都是需要显式传递cls的,而__init__在调用父类__init__方法时就是隐式的。#自定义元类classMyMeta(type):def__new__(cls,name,bases,dct):print(f"Creatingclass{name}usingMyMeta")......
  • [NewStarCTF 公开赛赛道]HTTP
    [NewStarCTF公开赛赛道]HTTPGET方式传入name源代码里发现key修改cookie......
  • Migrating to SAP S4HANA
    MigratingtoSAPS4HANA            ......
  • 文献阅读笔记|将H&E图像转换为虚拟免疫组化图像的病理学工具|Accelerating histopatho
    论文链接:https://doi.org/10.1038/s42256-024-00889-5论文信息:发表于NatureMachineIntelligence。2023年12月4日投稿,2024年7月29日接收,2024年9月9日online目录AbstractIntroduction1、从HE染色病理图像合成多重免疫组化(IHC)染色图像的意义2、虚拟染色【1】含义介绍【2】配对模......
  • ROS(Robot Operating System)
    ROS(RobotOperatingSystem)是一个开源的机器人中间件框架,提供了多种工具、库和约定,帮助开发者更高效地开发机器人应用程序。虽然名字中有"操作系统"(OperatingSystem),但ROS更像是一个操作系统层的框架,提供了机器人开发所需的基本功能,比如硬件抽象、设备驱动、底层控制、功能......
  • 一个js文件导出一个new class实例,其他多个地方import引用的是同一个实例对象吗
    在JavaScript中,当你从一个模块导出一个类的实例时,其他模块在导入这个实例时将获得该实例的一个引用。这意味着,如果你修改了这个实例的属性或调用它的方法,所有导入该实例的模块都会看到这些更改,因为它们引用的是同一个对象。以下是一个示例:moduleA.js:classMyClass{const......
  • new,apply,call,bind方法
    newnew被调用后做了什么创建一个空对象,该对象的__proto__属性应该指向new调用的构造函数的prototype将this指向这个空对象执行new调用的构造函数代码块内容根据调用的构造函数是否有返回值判断,如果返回值存在且typeof检测类型为object类型,则返回该结果,如果不存在返回值或者......
  • 深入解析 `Proxy.newProxyInstance` 方法的三个参数
    深入解析Proxy.newProxyInstance方法的三个参数在Java中,动态代理是通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现的。Proxy.newProxyInstance方法是创建动态代理实例的核心。为了更好地理解这个方法及其参数,我们将逐一探讨每个参数的作用,并结......