【题解】CF2029C New Rating
思路(二分 + 数据结构优化DP)
大致题意为:一个值
如果
如果
如果
必须且只能跨越一段连续区间,求
后面的由前面的计算得到,并且前面的不受后面的影响,并且必须跳跃一段,很容易想到 DP。
首先是 DP 状态。
最优解是开三个状态来 DP,这个官方题解讲得很清楚,因此这里提供一个我赛时写的两个状态的思路。
根据题目描述,可以连续也可以跳跃。
因此我选择给每个点
以下用
首先我们可以预处理一下一次也不跳的时候,每一个点的答案是多少,把它存入
然后我们考虑要跳跃的,也就是
因为题目要我们只能跳一次,所以,跳跃后的值只能通过没有跳过的值来转移,也就是
那么问题来了,只要和
题目要我们求的是最后结果的最大值,不难发现,贪心地想,最后结果要最大,每一个局部的结果也要尽可能大。
因为这样的话,在
于是我们只有在这个点是增的时候才会更优,也就是在前面选择一个
(简单证明:如果
又因为结果要尽可能的大,所以要在更小的点中选一个最大的。
那么转移方程就出来了:
不难发现,如果暴搜的话,这样的复杂度是
那再细品一下这句话:“比
我们可以把
我赛时使用的树状数组(赛后发现 set 或者 map 就可以了而且复杂度更优)。
每次转移时,二分找到小于
这个过程可以在循环的过程中同时进行,每次处理完一个
到这里,我们就处理出了每一个点不跳跃到达和只跳跃一次到达能够得到的最大值。
要得到最后答案,我们还需要从前往后来一轮 DP。
这里我们再开一个 DP 数组
那么转移也很明显。
因为
而
也就是假设跳跃到前面再不跳跃到
如果
如果
如果
所以
这里要特别注意一下,如果
最后取答案的时候,其实就是
时间复杂度
写题解的时候又想了一下,其实数据结构也可以不用,只需要记录到
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;
}
作者: 天天超方的
出处: https://www.cnblogs.com/TianTianChaoFangDe
关于作者:ACMer,算法竞赛爱好者
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显醒目位置给出, 原文链接 如有问题, 可邮件(1005333612@qq.com)咨询.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架