CF1474-D. Cleaning

题意:

给出一个长度为\(n\)的正整数序列,你可以对序列进行如下操作:

  • 对序列中相邻的两个数字\(a_{i}, a_{i+1}\)同时减去一个数字\(t(t<=min(a_{i},a_{i+1}))\)。

现在你有一次机会可以将序列中任意两个相邻的数字交换位置(可以不交换)。问你可不可以通过上述操作将序列中所有数字都减为\(0\)。


题解:

在说具体做法之前,先考虑三个事情:

1. 先不考虑交换数字,在不交换数字的情况我们要把序列中全部的数字都消为\(0\)(当然也可能不能全部消为0)可以通过什么方法呢?可以通过以下两种操作获得:

首先我们假设在\(a_1\)的前面有一个\(a_0=0\),那么消除的过程就是\(a_1=a_1-a_0, a_2=a_2-a_1,..., a_n=a_n-a_{n-1}\),这样顺利的话就可以把全部的数字消掉。

或者我们假设在\(a_n\)的后面有一个\(a_{n+1}=0\),那么过程为\(a_n=a_n-a_{n+1}, a_{n-1}=a_{n-1}-a_n,..., a_1=a_1-a_2\),同样也可以把全部的数字都消掉。

上述操作为顺利情况下消除的过程,但实际上并不一直是那么的顺利。以上述的第一种操作为例,假如有\(a_{i-1}>a_{i}\),那么就会出现\(a_{i}<0\)的情况,这显然是不可能的,同样第二种操作也可能会出现这种情况。

2. 如果从前往后减,那么在交换了\(a_{i},a_{i-1}\)这两个数字之后会不会对\(a_{i-1}\)之前的数字造成影响呢?同样的从后往前减,交换\(a_{i},a_{i+1}\)之后会不会对\(a_{i+1}\)之后的数字造成影响呢?这两个问题的答案是否定的,均不会造成影响。

3. 对于一个序列,假设现在这个序列可以从前往后把所有数字都消掉,那么先从前往后消掉一部分,那么可不可以从后往前把剩下的一部分全部消掉呢?再假设这个序列不能从前往后或从后往前把全部数字消掉,那么能不能通过先从前往后消掉一部分再从后往前消掉另一部分把整个序列消掉?

换句话说,对于一个序列而言,先从前往后消去一部分,再从后往前消去另一部分,是否和只从前往后或只从后往前消除是等效的呢(能全部消掉或不能)?答案是肯定的,这也是本题的关键。


前面作了那么多铺垫,现在说一下做法。先定义两个数组\(pre(prefix)\)和\(suf(suffix)\),\(pre[i]\)表示从前往后消掉了\(a_{1}, a_{2},..., a_{i-1}\)之后\(a_{i}\)的值,\(suf[i]\)表示从后往前消掉了\(a_{n}, a_{n-1},...,a_{i+1}\)之后\(a[i]\)的值。这里说一下,不论是从前往后还是从后往前,如果\(a_{i}\)减完之后得到了一个负数,那么他之后的所有数字不论是正是负都没有意义了,所以\(a_{i}\)以及\(a_{i}\)之后所有数字就需要用一个特殊的标记这个\(pre\)或\(suf\)是无效的。

现在就可以枚举交换的数字了,比如现在要枚举的是交换\(a_{i}\)和\(a_{i+1}\),那么只需要看\(pre[i-1],a[i+1],a[i],suf[i+2]\)这几个数字构成的序列可不可以通过从前往后或者从后往前给全部消掉即可,如果可以答案就是\(YES\),后面也就不用继续枚举了;如果全部枚举之后都不能那么答案就是\(NO\)。当然这有个前提就是\(pre[i-1]\)和\(suf[i+2]\)不能是无效的,也就是不能是你之前打过特殊标记的。


AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector> void solve() {
int n;
scanf("%d", &n);
std::vector<int>a(n + 2);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
std::vector<int>pre(n + 2), suf(n + 2);
for (int i = 1; i <= n; i++) {
if (pre[i - 1] == -1 || a[i] < pre[i - 1]) {
pre[i] = -1;
} else {
pre[i] = a[i] - pre[i - 1];
}
}
if (pre[n] == 0) {
printf("YES\n");
return;
}
for (int i = n; i > 0; i--) {
if (suf[i + 1] == -1 || a[i] < suf[i + 1]) {
suf[i] = -1;
} else {
suf[i] = a[i] - suf[i + 1];
}
}
for (int i = 0; i <= n - 2; i++) {
int l = i, r = i + 3;
if (pre[l] == -1 || suf[r] == -1) {
continue;
}
if (a[r - 1] >= pre[l] && a[l + 1] >= suf[r] && a[r - 1] - pre[l] == a[l + 1] - suf[r]) {
printf("YES\n");
return;
}
}
printf("NO\n");
} int main() {
int T;
scanf("%d", &T);
while(T--) {
solve();
} return 0;
}

小结:

本题本质上就是暴力,只不过\(O(n^2)\)的暴力是不能接受的,所以先对原来数据进行预处理,使得最终时间复杂度从\(O(n^2)\)减为\(O(n)\)。

CF1474-D. Cleaning的更多相关文章

  1. 【bzoj1672】[USACO2005 Dec]Cleaning Shifts 清理牛棚

    题目描述 Farmer John's cows, pampered since birth, have reached new heights of fastidiousness. They now ...

  2. Coursera-Getting and Cleaning Data-week1-课程笔记

    博客总目录,记录学习R与数据分析的一切:http://www.cnblogs.com/weibaar/p/4507801.html -- Sunday, January 11, 2015 课程概述 G ...

  3. Coursera-Getting and Cleaning Data-Week2-课程笔记

    Coursera-Getting and Cleaning Data-Week2 Saturday, January 17, 2015 课程概述 week2主要是介绍从各个来源读取数据.包括MySql ...

  4. Coursera-Getting and Cleaning Data-Week3-dplyr+tidyr+lubridate的组合拳

    Coursera-Getting and Cleaning Data-Week3 Wednesday, February 04, 2015 好久不写笔记了,年底略忙.. Getting and Cle ...

  5. Coursera-Getting and Cleaning Data-week4-R语言中的正则表达式以及文本处理

    博客总目录:http://www.cnblogs.com/weibaar/p/4507801.html Thursday, January 29, 2015 补上第四周笔记,以及本次课程总结. 第四周 ...

  6. 【BZOJ1672】[Usaco2005 Dec]Cleaning Shifts 清理牛棚 动态规划

    [BZOJ1672][Usaco2005 Dec]Cleaning Shifts Description Farmer John's cows, pampered since birth, have ...

  7. poj 2376 Cleaning Shifts

    http://poj.org/problem?id=2376 Cleaning Shifts Time Limit: 1000MS   Memory Limit: 65536K Total Submi ...

  8. POJ 2376 Cleaning Shifts(轮班打扫)

    POJ 2376 Cleaning Shifts(轮班打扫) Time Limit: 1000MS   Memory Limit: 65536K [Description] [题目描述] Farmer ...

  9. POJ 2376 Cleaning Shifts 贪心

    Cleaning Shifts 题目连接: http://poj.org/problem?id=2376 Description Farmer John is assigning some of hi ...

  10. Bzoj 3389: [Usaco2004 Dec]Cleaning Shifts安排值班 最短路,神题

    3389: [Usaco2004 Dec]Cleaning Shifts安排值班 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 218  Solved: ...

随机推荐

  1. LeetCode897. 递增顺序查找树

    题目 法一.自己 1 class Solution { 2 public: 3 vector<int>res; 4 TreeNode* increasingBST(TreeNode* ro ...

  2. Windows安全加固

    Windows安全加固 # 账户管理和认证授权 # 1.1 账户 # 默认账户安全 # 禁用Guest账户. 禁用或删除其他无用账户(建议先禁用账户三个月,待确认没有问题后删除.) 操作步骤 本地用户 ...

  3. Oracle数据库启动和关闭

    在介绍oracle数据库的启动和关闭前,先看一下Oracle的参数文件. oracle参数文件 1.初始化参数文件 oracle的初始化参数文件分为spfilesid.ora.spfile.ora.i ...

  4. libuv中实现tcp服务器

    目录 1.说明 2.libuv的tcp server 3.API简介 3.1.uv_tcp_init 3.2.uv_ip4_addr 3.3.uv_tcp_bind 3.4.uv_listen 3.5 ...

  5. 13 | 实战:单机如何实现管理百万主机的心跳服务? https://time.geekbang.org/column/article/240656

    13 | 实战:单机如何实现管理百万主机的心跳服务? https://time.geekbang.org/column/article/240656

  6. RAID系统被初始化

    RAID系统被初始化 https://forum.huawei.com/enterprise/zh/thread-256077-1-1.html

  7. 内存模型 Memory model 内存分布及程序运行中(BSS段、数据段、代码段、堆栈

    C语言中内存分布及程序运行中(BSS段.数据段.代码段.堆栈) - 秦宝艳的个人页面 - 开源中国 https://my.oschina.net/pollybl1255/blog/140323 Mem ...

  8. 目前 c++ primer学习总结笔记

    C++的开始 1 main的返回值:0为成功状态,非0为系统定义的错误类型 2 输入输出:计算结果为左侧运算对象,IO操作读写缓冲与程序中的动作无关 3 输入流istream对象:cin(标准输入): ...

  9. 线性DP总结(studying

    写在前面 虽然都说线性DP是入门,但我还是今天才开始学 线性DP就是珂以通过线性处理得出答案的一种DP 每一种状态都可以从前面推得,并且推导过程是呈线性的 参考题单(本人现在主要用luogu,所以这些 ...

  10. java创建线程的多种方式

    java创建线程的四种方式 1.继承 Thread 类 通过继承 Thread 类,并重写它的 run 方法,我们就可以创建一个线程. 首先定义一个类来继承 Thread 类,重写 run 方法. 然 ...