一道耗费大量精力的数据结构题目

题目链接

原题目
题目分析

大家应该都能意识到,每一次都去找下标最小的最大值是非常浪费时间的。而对于一个位置而言,只要被操作一次就等于这个位置要被操作a[i]次,因为两边的值同时被-1,导致两边的值永远不会被取到。
因此,我们可以对一个选定的位置(i)一直操作,直到a[i]变成0,此时a[i-1]和a[i+1]必定都变成0。如此反复,我我们的任务就变成了维护这个序列,求在这个序列中被选出的若干数的和。
这样,我们最初的思路就产生了。vis[]用于判断某一个位置是否被覆盖,对于每一次修改,在修改完成后从大到小进行排序,每选出一个位置就把两边的位置的vis置为true,如此反复,直到把所有位置都取出,则取出这些数的和就是这一次询问的答案。
复杂度是O(qnlogn)O(qnlogn)O(qnlogn)。
这样我们就可以获得至少15分的好成绩啦
面对满眼的TLE,再看着出题人是lxl,我们就知道这个题目需要一些毒瘤的数据结构来维护啦。

题目再分析

由于需要取的数字是一系列较大的数,所以我们会考虑到一些单调的序列上的性质。我们发现,对于一个单调序列,我们取到的数字必定是第一大,第三大,第五大……在下标上的规律就是,取到的数下标的奇偶性必定是相同的。所以我们考虑使用树状数组(也许线段树也可以)分奇偶维护序列ai的前缀和,这样面对单调的序列就可以快速求出对应的数字和了。
那么下一个问题就是确定每一段单调序列的位置,这个问题也不难解决,我们只需要一个set来维护每一段序列的开头位置和结尾位置(就相当于是维护这个序列的极大值和极小值的位置)即可。
尽管这样,每一次修改对全部内容进行更新也产生了非常不理想的时间复杂度。所以我们考虑一次修改产生的最大的影响有多大的范围,只修改会产生影响的范围内的数据就可以减小需要耗费的时间了。
经过艰苦的找数据过程,我们发现最多会影响到两边不超过2个极值点的位置,如序列(11,10,9,2,6,8)(11,10,9,2,6,8)(11,10,9,2,6,8),极值位置有(1,4,6)(1,4,6)(1,4,6),将第3位的9改成1,会导致[4,6][4,6][4,6]段变成[3,6][3,6][3,6]段,影响到了右数第二个极值位置(6);而对于一个上凸的序列(2,5,8,6,3)(2,5,8,6,3)(2,5,8,6,3),极值位置有(1,3,5)(1,3,5)(1,3,5),修改第四位的6为9,影响到了[1,3][1,3][1,3]段,变为了[1,4][1,4][1,4]段,影响了向左数第二个极值点。
所以对于每一次修改,我们都把它两边的两个极值点之间的区间进行更新,就可以保证全部覆盖每一次修改的影响范围。而答案自然就是(上一次的答案-之前的区间的答案贡献 + 修改之后的答案贡献)。
在大框架完成之后,需要考虑的问题是极小值是否需要计算的问题。方法是先都不计算极小值的部分,需要计算的情况只有与左右两边的极大值之间距离均为偶数(下标奇偶性相同),所以在计算的时候加一步讨论就可以解决。同时计算的时候还需要分递增序列和递减序列进行考虑
其他还有许许多多的边界问题需要考虑和讨论,在这部分我出锅的主要原因是set中begin()是真实存在的,而end()是不存在的。统计答案的时候端点达到begin是可以的,但是达到end是不可以的。
在具体实现上,我们发现输入原始序列就相当于是对一个全0的序列进行若干次修改,所以也同样可以使用上面的方法,这样也减轻了代码的压力

XBB

菜鸡讨论了一下午,终于解决了需要考虑的所有问题,A掉了两年前加到题单里的题目,将近3KB的代码QAQ

代码已经不成人形了5555

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int MAXN = 1e5 + 100;
typedef long long ll;
typedef set<int>::iterator SIT;
#define DEBUG cerr << "OK";
int a[MAXN],n;
struct TreeArray{ll s[MAXN];int N;inline int lowbit(int num){return num & (-num);}void add(int pos, int val){for (int i = pos; i <= N; i += lowbit(i))s[i] += 1LL * val;}ll query(int pos){int cnt = 0;ll sum = 0; for (int i = pos ; i; i -= lowbit(i),cnt++)sum += s[i];return sum;}
}arr[2];set<int> s;
ll ans;inline int re()
{int num = 0, minus = 1;char ch = getchar();while (ch > '9' || ch < '0'){if (ch == '-')minus = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){num = (num << 1) + (num << 3) + ch - '0';ch = getchar();}return num * minus;
}// get the peak or valley sum
ll getSum(SIT l, SIT r)
{ll ret = 0;while (l != r){SIT it = --r;++r;if(a[*it] < a[*r]){//递增int curPos = *r;ret += arr[curPos & 1].query(curPos) - arr[curPos & 1].query(*it);}else {//递减int curPos = *it;ret += arr[curPos & 1].query(*r - 1) - arr[curPos & 1].query(curPos);//极小值是否需要被加SIT moreRight = ++r;--r;SIT moreLeft = r;if (moreLeft != s.begin()) moreLeft--;if (moreRight == s.end())moreRight --;if (!((*moreRight - *r) & 1) && !((*r - *moreLeft) & 1))ret += a[*r];}r--;}if (a[*l] >= a[*l + 1])return ret;//判断边界最小值是否需要加,跟上面完全一样SIT moreRight = ++r;--r;SIT moreLeft = r;//moreLeft --;if (moreLeft != s.begin()) moreLeft--;if (moreRight == s.end())moreRight --;if (!((*moreRight - *r) & 1) && !((*r - *moreLeft) & 1))ret += a[*r];return ret;
}
inline void checkValid(int pos)
{if((a[pos] > a[pos - 1]) == (a[pos + 1] > a[pos]))s.erase(pos);else s.insert(pos);
}void update(int pos, int num)
{//to get the influenced segmentSIT it = s.lower_bound(pos);SIT r = it,l=it;r++,l--;if (r != s.end() && (*it) == pos) r++;if(r == s.end())r--;if (l != s.begin())l--;ans -= getSum(l, r);//更新内容arr[pos & 1].add(pos, num - a[pos]);a[pos] = num;checkValid(pos);if(pos != 1) checkValid(pos - 1);if(pos != n) checkValid(pos + 1);ans += getSum(l, r);
}   int main()
{n = re();arr[0].N = arr[1].N =n + 1;int tmp = 0;s.insert(0);s.insert(n + 1);for (int i = 1; i <= n; ++i){tmp = re();update(i,tmp);}int q = re();//for(SIT i = s.begin();i != s.end();++i)// cerr << *i << ' ';while(q--){int pos = re(), val = re();update(pos, val);printf("%lld\n",ans);}return 0;
}

(Ynoi2015) 纵使日薄西山 题解相关推荐

  1. [Ynoi2015]纵使日薄西山

    题目大意: 给定一个序列,每次单点修改,然后进行询问. 定义一次操作为,选择一个位置$x$,将这个位置的数和左边.右边两个位置的数(不存在则忽略)各减去1,然后和0取max. 对序列中最大的位置进行一 ...

  2. 【树状数组】【P5069】[Ynoi2015]纵使日薄西山

    Description 给定一个长度为 \(n\) 的非负整数序列 \(\{a_n\}\),\(q\) 次操作,每次要么单点修改序列某个值,要么查询整个序列需要操作多少次才能变成全 \(0\). 一次 ...

  3. 洛谷P5069 [Ynoi2015]纵使日薄西山(树状数组,set)

    洛谷题目传送门 一血祭 向dllxl致敬! 算是YNOI中比较清新的吧,毕竟代码只有1.25k. 首先我们对着题意模拟,寻找一些思路. 每次选了一个最大的数后,它和它周围两个数都要减一.这样无论如何, ...

  4. Luogu P5069 [Ynoi2015] 纵使日薄西山

    一开始想用线段树维护, 然后发现太多细节了,QAQ. 我们 可以发现一个数可以被取到最大值, 那么直接把他删到0不会影响结果. 因为 两边的位置都不能能取到最大值并影响当前位置. 所以我们只需要知道有 ...

  5. 【题解】Luogu P5071 [Ynoi2015]此时此刻的光辉

    众所周知lxl是个毒瘤,Ynoi道道都是神仙题,题面好评 原题传送门 一看这题没有修改操作就知道这是莫队题(我也只会莫队) 我博客里对莫队的简单介绍 一个数N可以分解成\(p_1^{c_1}p_2^{ ...

  6. 洛谷P5071 [YNOI2015]此时此刻的光辉 莫队+玄学优化+卡常QWQ

    题目链接:传送门 在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去.逐渐消逝的未来.我回来了,纵使日薄西山,即便看不到未来,此时此 ...

  7. 洛谷P5072 [YNOI2015]盼君勿忘 莫队+unordered_set+毒瘤卡常

    在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去.逐渐消逝的未来.我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘 ...

  8. [Ynoi2015]即便看不到未来

    题目大意: 给定一个序列,每次询问,给出一个区间$[l,r]$. 设将区间内的元素去重后重排的数组为$p$,求$p$中长度为$1\sim 10$的极长值域连续段个数. 长度为$L$的极长值域连续段的定 ...

  9. [JS][dfs]题解 | #迷宫问题#

    题解 | #迷宫问题# 题目链接 迷宫问题 题目描述 定义一个二维数组 N*M ,如 5 × 5 数组下所示: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 1, 1 ...

最新文章

  1. Android小知识-电量优化WakeLock的使用
  2. 物联网改变生活——飞思卡尔技术论坛中国站侧记
  3. Ext JS 3.0 RC1.1 Released发布了
  4. 【MM配置】Pricing 采购定价 1
  5. 【BZOJ 1266】 [AHOI2006]上学路线route
  6. QPixmap、QImage、QPicture、QBitmap四者区别
  7. Java面试中,一些常见的有关多线程问题!
  8. c# 拷贝结构体_C#学习之Partial、结构体、枚举
  9. python变量标识符_简谈-Python的注释、变量类型、标识符及关键字
  10. doxygen相关问题
  11. python模式匹配算法_详解Python 最短匹配模式
  12. Traceroute程序
  13. java 23种设计模式(转载)
  14. sqlmap-学习1 配置环境
  15. Eureka安全访问
  16. Google Go TCP 开发
  17. 什么是工业机器人,工业机器人的分类与特点
  18. EMNLP 2020 BiST: Bi-directional Spatio-Temporal Reasoning for Video-Grounded Dialogues
  19. QT中的sender函数,result函数
  20. P1293 班级聚会

热门文章

  1. 用C语言实现简单的猜数字小游戏
  2. MagicLicense破解记录
  3. 标称型和数值型(连续型)的区别
  4. ogr2ogr矢量数据要素格式转换
  5. Unity 打包图集
  6. 电子商务系统数据库设计(一)
  7. java雪花_基于java实现的雪花算法
  8. css 手抓,CSS 像素画 - 手和箭头
  9. c语言程序设计名片管理系统,《名片管理系统》 - C语言课程设计
  10. Git之深入解析如何替换数据库中的Git对象