CF2029C New Rating
思路(二分 + 数据结构优化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\) 压入一个便于二分数据结构中,比如 map 或 set。
我赛时使用的树状数组(赛后发现 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;
}
CF2029C New Rating的更多相关文章
- Codefroces 750C:New Year and Rating(思维)
http://codeforces.com/contest/750/problem/C 题意:有n场比赛,每场比赛有一个c,代表比赛结束后分数的增长情况,有一个d,代表这场比赛在div1或者div2打 ...
- AngularJs的UI组件ui-Bootstrap分享(十二)——Rating
Rating是一个用于打分或排名的控件.看一个最简单的例子: <!DOCTYPE html> <html ng-app="ui.bootstrap.demo" x ...
- 从Elo Rating System谈到层次分析法
1. Elo Rating System Elo Rating System对于很多人来说比较陌生,根据wikipedia上的解释:Elo评分系统是一种用于计算对抗比赛(例如象棋对弈)中对手双方技能水 ...
- HDU 4870 Rating(概率、期望、推公式) && ZOJ 3415 Zhou Yu
其实zoj 3415不是应该叫Yu Zhou吗...碰到ZOJ 3415之后用了第二个参考网址的方法去求通项,然后这次碰到4870不会搞.参考了chanme的,然后重新把周瑜跟排名都反复推导(不是推倒 ...
- Elo rating system 模拟
package org.cc.foo_008; import java.util.ArrayList; import java.util.List; import java.util.Random; ...
- HDU4870 Rating(概率)
第一场多校,感觉自己都跳去看坑自己的题目里去了,很多自己可能会比较擅长一点的题目没看,然后写一下其中一道概率题的题解吧,感觉和自己前几天做的概率dp的思路是一样的.下面先来看题意:一个人有两个TC的账 ...
- HDU 4870 Rating 概率DP
Rating Time Limit:5000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Statu ...
- hdu 4870 Rating
题目链接:hdu 4870 这题应该算是概率 dp 吧,刚开始看了好几个博客都一头雾水,总有些细节理不清楚,后来看了 hdu 4870 Rating (概率dp) 这篇博客终于有如醍醐灌顶,就好像是第 ...
- HDU 4870 Rating(高斯消元 )
HDU 4870 Rating 这是前几天多校的题目,高了好久突然听旁边的大神推出来说是可以用高斯消元,一直喊着赶快敲模板,对于从来没有接触过高斯消元的我来说根本就是一头雾水,无赖之下这几天做DP ...
- 2014多校第一场J题 || HDU 4870 Rating(DP || 高斯消元)
题目链接 题意 :小女孩注册了两个比赛的帐号,初始分值都为0,每做一次比赛如果排名在前两百名,rating涨50,否则降100,告诉你她每次比赛在前两百名的概率p,如果她每次做题都用两个账号中分数低的 ...
随机推荐
- Nuget Reference 丢失问题
现象 在 Visual Studio 2017 中创建一个控制台项目.创建出来的项目如下所示. 通过 NuGet 管理器,添加 Newtonsoft.Json 的 NuGet 包,安装之后,项目中添加 ...
- django静态文件、form表单和request对象
目录 一.静态文件 1.静态文件概念 2.资源访问 3.静态文件资源访问 二.静态文件相关配置 1.接口前缀 2.接口前缀动态匹配 三.form表单 action属性 method属性 四.reque ...
- 【前端】HTML编码效提升:快速生成HTML标签
目录 1.生成多级标签 2.生成同级标签 3.生成注释 4.生成多个相同标签 5.生成带class标签 6生成带id标签. 7.生成带内容标签1 8.生成带内容标签2 9.生成带属性标签 GIF演示: ...
- Macos 安装md5sum、sha1sum、md5deep、sha1deep
一.安装md5sum和sha1sum 方法一:brew 安装 # brew install md5sha1sum 方法二:编译安装 源码下载地址:http://www.microbrew.org/to ...
- IntelliJ IDEA生成jar包运行报Error:A JNI error has occurred,please check your installation and try again
首先介绍一下IntelliJ IDEA生成jar包的方式: 1.打开项目,打开FIile->Project Structure...菜单.如下图: 选中Artifacts,点+号,选择JAR,再 ...
- Qt/C++视频监控Onvif工具/组播搜索/显示监控画面/图片参数调节/OSD管理/祖传原创
一.前言 能够写出简单易用而又不失功能强大的组件,一直是我的追求,简单主要体现在易用性,不能搞一些繁琐的流程和一些极难使用的API接口,或者一些看不懂的很难以理解的函数名称,一定是要越简单越好.功能强 ...
- Qt/C++音视频开发58-逐帧播放/上一帧下一帧/切换播放进度/实时解码
一.前言 逐帧播放是近期增加的功能,之前也一直思考过这个功能该如何实现,对于mdk/qtav等内核组件,可以直接用该组件提供的接口实现即可,而对于ffmpeg,需要自己处理,如果有缓存的数据的话,可以 ...
- Qt编写的视频播放综合应用示例(qmedia/ffmpeg/vlc/mpv/海康sdk等)
一.功能特点 1.1 基础功能 支持各种音频视频文件格式,比如mp3.wav.mp4.asf.rm.rmvb.mkv等. 支持本地摄像头设备,可指定分辨率.帧率. 支持各种视频流格式,比如rtp.rt ...
- 零基础IM开发入门(四):什么是IM系统的消息时序一致性?
本文引用了沈剑<如何保证IM实时消息的"时序性"与"一致性"?>一文的图片和内容(由于太懒,图没重新画),原文链接在文末. 1.引言 本文接上篇&l ...
- WPF中实现弹出进度条窗口
实现功能: 模拟一个任务开始执行,在窗口弹出一个进度条,展示执行进度,执行完成弹出提示框.例如做数据查询时,如果查询需要一段时间,操作人员可以很好的知道是否查询完成. 1. 设计进度条弹出窗口 进度条 ...