题目

描述


题目大意

一个序列,每次可以使一段区间内的所有数加一(模四)。

问最少的操作次数。


思考历程

一看这题目,诶,这不就是那道叫密码锁的题目吗?

然后随便打一打,样例过了,就再也没有思考这一题。


正解

其实我的想法完全错了。

因为这题只能加,不能减啊!

于是就得考虑另一个方法。

题目可以转成这样的问题:给你一个数列,你可以预先给其中的数加四,然后每次对一个区间进行减一操作,问最少的操作数。

显然,如果已经加四了,就是一道大水题(好像叫……粉刷栅栏)?

我们先不考虑加四,那么答案就是∑max(ai−ai−1,0)\sum{max(a_i-a_{i-1},0)}∑max(ai​−ai−1​,0)

然后我们考虑加四会有什么影响。

现在我们考虑一下,假设有两个高地为lll和rrr,中间的比较低,要把它们降下来,能不能通过抬高中间的,使得操作数尽量小呢?

然后开始按照lll和l+1l+1l+1之差和r−1r-1r−1和rrr之差分类讨论。

接着就可以发现,只有(−3,3)(-3,3)(−3,3)(−2,3)(-2,3)(−2,3)(−3,2)(-3,2)(−3,2)的情况是有意义的。

于是我们扫一扫有没有这样的东西,减去它们的贡献就好了。

要注意,如果一起做,有可能搞完了(−2,3)(-2,3)(−2,3),就没办法搞(−3,3)(-3,3)(−3,3)了。

由于(−3,3)(-3,3)(−3,3)更优,所以先从左到右将它给搞掉。

接着重新搞剩下两种。

然后就可以做出来了,说实在的,这方法让我醉了……

还有一种比较强大的做法,没有分类讨论。

刚开始的操作是一样的,同样是计算出一个暂时的答案。

然后从前往后扫,如果现在走的是下坡路,就将高度差ai−ai−1+4a_i-a_{i-1}+4ai​−ai−1​+4存入一个桶中。

如果在走上坡路,记高度差为xxx,就在桶种找小于xxx的第一个有值的,记为jjj。

如果找到了就让答案减去x−jx-jx−j,然后xxx在桶中的值减一,jjj在桶中的值加一。

这样就可以计算出答案了。

具体原因什么的……感觉上有些玄学,我是感性理解的。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
int n;
int a[N],c[N];
int las[N];
int main(){
int T;
scanf("%d",&T);
while (T--){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<=n;++i){
int x;
scanf("%d",&x);
a[i]=(x-a[i]+4)%4;
}
int ans=0;
for (int i=0;i<=n;++i){
ans+=max(a[i+1]-a[i],0);
c[i]=a[i+1]-a[i];
}
memset(las,255,sizeof las);
int cnt2=0,cnt3=0;
for (int i=0,j=-1;i<=n;++i)
if (c[i]==-3){
cnt3++;
las[i]=j;
j=i;
}
else if (c[i]==3 && cnt3){
cnt3--;
c[j]=c[i]=0;
j=las[j];
ans-=2;
}
cnt3=0;
for (int i=0;i<=n;++i)
if (c[i]==-2)
cnt2++;
else if (c[i]==-3)
cnt3++;
else if (c[i]==2 && cnt3)
cnt3--,ans--;
else if (c[i]==3 && cnt2)
cnt2--,ans--;
printf("%d\n",ans);
}
return 0;
}
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
int n;
int a[N];
int buc[4];
int main(){
int T;
scanf("%d",&T);
while (T--){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<=n;++i){
int x;
scanf("%d",&x);
a[i]=(x-a[i]+4)%4;
}
int ans=0;
for (int i=1;i<=n;++i)
ans+=max(a[i]-a[i-1],0);
memset(buc,0,sizeof buc);
for (int i=1;i<=n;++i)
if (a[i-1]>a[i])
buc[a[i]-a[i-1]+4]++;
else{
int j=0;
for (;j<a[i]-a[i-1];++j)
if (buc[j])
break;
if (buc[j]){
buc[j]--,buc[a[i]-a[i-1]]++;
ans-=a[i]-a[i-1]-j;
}
}
printf("%d\n",ans);
}
return 0;
}

总结

分类讨论是一种很恶心的方法……

另一种说法叫做:面向数据编程

[JZOJ4788] 【NOIP2016提高A组模拟9.17】序列的更多相关文章

  1. 【JZOJ4788】【NOIP2016提高A组模拟9.17】序列

    题目描述 输入 输出 样例输入 1 5 2 1 3 0 3 2 2 0 1 0 样例输出 1 数据范围 解法 考虑没有模的情况,问题就仅仅只是简单的差分问题(广告铺设): 设r[i]是第i位需要加的次 ...

  2. 【JZOJ4787】【NOIP2016提高A组模拟9.17】数格子

    题目描述 输入 输出 样例输入 1 10000 3 10000 5 10000 0 0 样例输出 1 11 95 数据范围 每个测试点数据组数不超过10组 解法 状态压缩动态规划. 设f[i][j]表 ...

  3. 【NOIP2016提高A组模拟9.17】序列

    题目 分析 首先用\(a_i\)表示达到目标的步数\(B_i-A_i(mod 4)\) 根据粉刷栅栏,先不管mod 4的情况,答案就是\(\sum\max(a_i-a_{i+1},0)\) 那我们刚才 ...

  4. NOIP2016提高A组模拟9.17总结

    第一题,典型的隔板问题, 但是我忘记隔板问题怎么打,一开始在花了1小时,还是没想出来,果断弃疗, 最后的40分钟,我打完了第二题,接着又用了20分钟推敲出一种极其猥琐的式子来代替,可惜预处理的阶乘忘记 ...

  5. 【NOIP2016提高A组模拟9.17】数格子

    题目 分析 设表示每一行的状态,用一个4位的二进制来表示,当前这一行中的每一个位数对下一位有没有影响. 设\(f_{i,s}\)表示,做完了的i行,其状态为s,的方案数. 两个状态之间是否可以转移就留 ...

  6. 【NOIP2016提高A组模拟9.17】小a的强迫症

    题目 分析 题目要求第i种颜色的最后一个珠子要在第i+1种颜色的最后一个珠子之前, 那么我们从小到大枚举做到第i种,把第i种的最后一颗珠子取出,将剩下的\(num(i)-1\)个珠子插入已排好的前i- ...

  7. 【NOIP2016提高A组模拟8.17】(雅礼联考day1)总结

    考的还ok,暴力分很多,但有点意外的错误. 第一题找规律的题目,推了好久.100分 第二题dp,没想到. 第三题树状数组.比赛上打了个分段,准备拿60分,因为时间不够,没有对拍,其中有分段的20分莫名 ...

  8. 【NOIP2016提高A组模拟8.17】(雅礼联考day1)Binary

    题目 分析 首先每个数对\(2^i\)取模.也就是把每个数的第i位以后删去. 把它们放进树状数组里面. 那么当查询操作, 答案就位于区间\([2^i-x,2^{i-1}-1-x]\)中,直接查询就可以 ...

  9. 【NOIP2016提高A组模拟8.17】(雅礼联考day1)Value

    题目 分析 易证,最优的答案一定是按\(w_i\)从小到大放. 我们考虑dp, 先将w从小到大排个序,再设\(f_{i,j}\)表示当前做到第i个物品,已选择了j个物品的最大值.转移就是\[f_{i, ...

随机推荐

  1. js 关闭页面(Scripts may close only the windows that were opened by it.)

    传送http://blog.csdn.net/kuangfengbuyi/article/details/52052301 js关闭当前页面,当该页面不是其他页面打开的,而是直接输入url, 直接用w ...

  2. ELK日志分析系统(原创)

    一.简介 ELK由Elasticsearch.Logstash和Kibana三部分组件组成: Elasticsearch是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引 ...

  3. 【转】详解tomcat的连接数与线程池

    对tomcat线程池.Connector的BIO.NIO解析的很透彻的一篇文章. 原文链接:https://www.cnblogs.com/kismetv/p/7806063.html 前言 在使用t ...

  4. D3.js坐标轴的绘制方法、添加坐标轴的刻度和各比例尺的坐标轴(V3版本)

    坐标轴(Axis)   坐标轴(Axis)在很多图表中都可见到,例如柱形图.折线图.散点图等.坐标轴由一组线段和文字组成,坐标轴上的点由一个坐标值确定.但是,如果使用SVG的直线和文字一笔一画的绘制坐 ...

  5. Docker学习のDocker的简单应用

    一.常见基本docker命令 docker是在一个linux虚拟机上运行的(对于windows来说),打开Docker quickStart terminal,就连街上了docker的 daemon ...

  6. pytorch clamp 与clamp_区别

    pytorch clamp 与clamp_ ,有下划线的表示修改并付给自身,无下划线的表示需要返回处理后的值,比如: h = k.clamp(min=0) #将结果存入h,k保留原值 k.clamp_ ...

  7. Haar分类器方法

    一.Haar分类器的前世今生 二.人脸检测属于计算机视觉的范畴,早期人们的主要研究方向是人脸识别,即根据人脸来识别人物的身份,后来在复杂背景下的人脸检测需求越来越大,人脸检测也逐渐作为一个单独的研究方 ...

  8. CI的session操作

    在使用session之前,要对配置文件config.php 里面的$config['encryption_key']随便赋个值,例如1234 1. 首先要加载session类,固定写法:$this-& ...

  9. MySQL高可用(Galera Cluster)

    Galera Cluster简介 Galera Cluster是集成了Galera插件的MySQL集群,是一种新型的,数据不共享的,高度冗余的高可用方案,目前Galera Cluster有两个版本,分 ...

  10. Delphi 窗口置顶的方法

    有几种窗口置顶的方法,简单的有: ShowWindow(窗口句柄,sw_ShowNormal); SetWindowPos(窗口句柄,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOV ...