题目

描述


题目大意

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

问最少的操作次数。


思考历程

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

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


正解

其实我的想法完全错了。

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

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

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

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

我们先不考虑加四,那么答案就是∑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. hexo next主题深度优化(八),微加速

    个人博客:https://mmmmmm.me 源码:https://github.com/dataiyangu/dataiyangu.github.io 通过不断地上网查资料,引用的js.css.图片 ...

  2. AtCoder ABC 126F XOR Matching

    题目链接:https://atcoder.jp/contests/abc126/tasks/abc126_f 题目大意 给定两个整数 M 和 K ,用小于 2M 的的所有自然数,每个两个,用这些数排成 ...

  3. 802.11ac wave2的前世今生

    2015年下半年,高通.博通.RTL等芯片厂商相继发布了满足802.11ac wave2要求的芯片,WLAN及终端厂商也迅速跟进推出相应的产品和终端.802.11ac wave2在多方推动下于2015 ...

  4. scanf 与getchar区别

    #include<stdio.h> void main() {    int c; c=getchar(); //scanf("%c",&c); if(c!=' ...

  5. 在Ubuntu中安装MySQL (转载)

    MySQL在Linux Ubuntu中安装 本文使用的Linux是Ubuntu 12.04.2 LTS 64bit的系统,安装MySQL数据库软件包可以通过apt-get实现. 在Linux Ubun ...

  6. python读取Excel表格文件

    python读取Excel表格文件,例如获取这个文件的数据 python读取Excel表格文件,需要如下步骤: 1.安装Excel读取数据的库-----xlrd 直接pip install xlrd安 ...

  7. 01、requests 基本使用

    requests模块的基本使用 基于网络请求的模块. 环境的安装:pip install requests 作用:模拟浏览器发起请求 分析requests的编码流程: 1.指定url 2.发起了请求 ...

  8. Jmeter-----请求依赖之JsonExtractor

    层级关系填写: 1.第一个必须是$ 2.用英文状态下的  . 来代表下一个层级

  9. VMware 安装android-x86系统。

    首先先安装 VMware 虚拟机,并下载 android-x86_64-8.1-r2.iso 系统. VMware安装完成后,打开VMware Workstation,单击“创建新的虚拟机”,或者在菜 ...

  10. Spark中的各种action算子操作(java版)

    在我看来,Spark编程中的action算子的作用就像一个触发器,用来触发之前的transformation算子.transformation操作具有懒加载的特性,你定义完操作之后并不会立即加载,只有 ...