Description

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

一次操作是指:找到序列的最大值的位置,如果有多个最大值则取最左边的,然后将这个数和这个位置左右紧挨着的数都 \(-1\),如果减到 \(0\) 则不减。

Limitation

\(1~\leq~n,~q~\leq~10^5\)

序列值域在 \(10^9\) 范围内。

Solution

第一次写 YnOI 体验极差

首先注意到的是如果确定要操作某个位置 \(x\) 了,那么最终这个位置一定被操作了 \(a_x\) 次。原因是这个位置每操作一次,左右都会减一,这样这个位置永远不会因为要操作左右而减小,所以让这个位置变成 \(0\) 的唯一可能只有操作这个位置本身。于是找到一个位置将它一直操作到 \(0\) 与原操作方式是等价的。于是问题被转化成了求所有会被操作位置的数字和。

先设序列中的数互不相同。考虑序列中一个单调递增或者单调递减的极长子序列,首先会操作最大值,减到 \(0\) 以后次大值一定也被减到了 \(0\),然后再操作次次大值,然后次次次大也减到 \(0\) 了……一直这么下去发现所有被操作位置的奇偶性是相同的,即在这个极长单调序列中所有位置与最大值位置的奇偶性相同的点都会被操作。考虑用 set 维护序列中所有极值点,用两个树状数组分别奇数下标的前缀和和偶数下标的前缀和,即可求出初始序列的操作次数。

考虑修改的时候,只需要继续修改序列的极值点即可。

于是你就要面对这个点本来是极值点,本来不是极值点。他左边是极值点,右边是极值点,都不是极值点,改完以后极值位置不变,改完以后新增一个极大值,新增一个极小值等等十几种情况

考虑手动讨论每种情况显然非常不靠谱,但是注意到修改某个位置最多只会影响到这个位置向左右分别数两个极值点(不包括自身)这段区间的情况,同时新可能增加或者删除的极值点只有这个位置和这个位置左右各一个位置共三个位置。于是可以暴力重构这段区间的答案,由于求答案的时候只需要扫描区间内所有极值点,而极值点个数又是常数级别的,于是可以 \(O(\log n)\) 的去修改。这个 \(\log\) 是由于 setlower_bound 造成的。

解释一下为什么会影响到左右数第二个极值点,如下数据:

9 2 6 8

三个极值点分别是 \(1,2,4\),其中 \(2\) 是极小值,\(1,4\) 是极大值。如果将位置 \(1\) 修改为 \(1\),那么极值点变成了 \(1,4\),由此,\([2,4]\) 这段区间不复存在,变成了 \([1,4]\) 这段区间,需要重新统计,影响到了 \(1\) 向右数第 \(2\) 个极值点 \(4\)。

考虑如果序列中有相同的数怎么办:

如果相同的数不连续,那么根本不用管。如果连续,先假设这个这组数的第一个数大于左侧的数,那么操作到这组数的时候,这个数会被最先操作,于是这组数的第一个位置应该被设为极大值。如果这组数的第一个数小于左侧的数,那么第一个数是否被操作不由这个数决定,它不应该成为极小值。

考虑这组数的最后一个数,如果它大于右侧的数,那么这个数操作完以后会继续操作右侧的数,它不应该成为极大值。如果它小于左侧的数,那么左右两侧的区间都会在操作到这个位置的时候停止,那么这个位置应该成为极小值。

有一些细节:

考虑一个极小值会被操作,当且仅当它左右的数都没有被操作,那么它不会被减掉,只能自己单独操作。这种情况即它到左右的极大值点的距离都是偶数。

在扫描区间答案的时候,如果区间有 \(x\) 个极值点,那么只需要考虑 \(x-1\) 段区间的答案,因此只需要扫描 \(x-1\) 个极值点。但是需要注意到的是不能漏掉剩下那个极值点是极小值的讨论。

Code

参考了 @FlushHu 神仙的代码,在此表示感谢。

#include <cstdio>
#include <set>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif typedef long long int ll; namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
} template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
} namespace OPT {
char buf[120];
} template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
} const int maxn = 100005; int n, dn;
ll ans;
ll MU[maxn], odd[maxn], eve[maxn];
std::set<int>s; void check(const int x);
void upd(const int x);
void calc(const int ll, const int rr, const int v); int main() {
freopen("1.in", "r", stdin);
qr(n); dn = n + 1;
s.insert(0); s.insert(n + 1);
for (int i = 1; i <= n; ++i) {
upd(i);
}
int q = 0, x;
qr(q);
while (q--) {
x = 0; qr(x);
upd(x);
printf("%lld\n", ans);
}
return 0;
} inline int lowbit(const int x) { return x & -x; } void update(ll *const a, int p, const ll v) {
while (p <= dn) {
a[p] += v;
p += lowbit(p);
}
} ll query(ll *const a, int p) {
ll _ret = a[p];
do _ret += a[p -= lowbit(p)]; while (p);
return _ret;
} void upd(const int x) {
ll y = 0; qr(y);
auto v = s.lower_bound(x);
auto l = v, r = v;
--l; ++r;
if (l != s.begin()) --l;
if ((r != s.end()) && (*v == x)) ++r;
if (r == s.end()) --r;
calc(*l, *r, -1);
update(x & 1 ? odd : eve, x, y - MU[x]); MU[x] = y;
check(x);
if (x != 1) check(x - 1);
if (x != n) check(x + 1);
calc(*l, *r, 1);
} void check(const int x) {
if ((MU[x] > MU[x - 1]) != (MU[x] < MU[x + 1])) s.insert(x);
else s.erase(x);
} void calc(const int ll, const int rr, const int v) {
auto l = s.lower_bound(ll), r = s.lower_bound(rr);
while (r != l) {
auto t = r; --t;
if (MU[*t] < MU[*r]) {
auto ad = *r & 1 ? odd : eve;
ans += v * (query(ad, *r) - query(ad, *t));
} else {
auto ad = *t & 1 ? odd : eve;
ans += v * (query(ad, *r - 1) - query(ad, *t));
auto l0 = r, r0 = r;
if (l0 != s.end()) --l0;
++r0; if (r0 == s.end()) --r0;
if ((!((*r - *l0) & 1)) && (!((*r0 - *r) & 1))) {
ans += MU[*r] * v;
}
}
--r;
}
if (MU[*l] >= MU[*l + 1]) return;
auto l0 = r, r0 = r;
if (l0 != s.end()) --l0;
++r0; if (r0 == s.end()) --r0;
if ((!((*r - *l0) & 1)) && (!((*r0 - *r) & 1))) {
ans += MU[*r] * v;
}
}

【树状数组】【P5069】[Ynoi2015]纵使日薄西山的更多相关文章

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

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

  2. BZOJ 1103: [POI2007]大都市meg [DFS序 树状数组]

    1103: [POI2007]大都市meg Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2221  Solved: 1179[Submit][Sta ...

  3. bzoj1878--离线+树状数组

    这题在线做很麻烦,所以我们选择离线. 首先预处理出数组next[i]表示i这个位置的颜色下一次出现的位置. 然后对与每种颜色第一次出现的位置x,将a[x]++. 将每个询问按左端点排序,再从左往右扫, ...

  4. codeforces 597C C. Subsequences(dp+树状数组)

    题目链接: C. Subsequences time limit per test 1 second memory limit per test 256 megabytes input standar ...

  5. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  6. BZOJ 3529: [Sdoi2014]数表 [莫比乌斯反演 树状数组]

    3529: [Sdoi2014]数表 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1399  Solved: 694[Submit][Status] ...

  7. BZOJ 3289: Mato的文件管理[莫队算法 树状数组]

    3289: Mato的文件管理 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 2399  Solved: 988[Submit][Status][Di ...

  8. 【Codeforces163E】e-Government AC自动机fail树 + DFS序 + 树状数组

    E. e-Government time limit per test:1 second memory limit per test:256 megabytes input:standard inpu ...

  9. 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序

    3881: [Coci2015]Divljak Time Limit: 20 Sec  Memory Limit: 768 MBSubmit: 508  Solved: 158[Submit][Sta ...

随机推荐

  1. Java代码质量检查checkstyle, pmd, cpd, p3c,findbugs, jacoco, sonarquebe以及和Jenkins集成

    概述 又搞一边质量扫描插件,之前做过一遍,然后后面各种忽略,然后就放弃了,所以,应该寻找一种方法,循序渐进的实施.本次将实施一个基本的打包扫描方案,包含 checkstyle 固定团队编码风格,固定命 ...

  2. CAS5单点登录

    看这篇文章即可:https://www.jianshu.com/p/c1273d81c4e4>https://www.jianshu.com/p/c1273d81c4e4

  3. 百度前端技术学院task1.10

    任务十:Flexbox 布局练习 面向人群: 有一定HTML及CSS基础的同学 难度: 中 重要说明 百度前端技术学院的课程任务是由百度前端工程师专为对前端不同掌握程度的同学设计.我们尽力保证课程内容 ...

  4. go ---MQTT client

    Paho GO Client   语言 GO 协议 EPL AND EDL 官网地址 http://www.eclipse.org/paho/ API类型 Asynchronous  描述 Paho ...

  5. Redis-1-简介与安装

    目录 1.Redis 简介 2.安装Redis 1.安装gcc redis是c语言编写的 2.下载redis安装包,在root目录下执行 3.解压redis安装包 4.进入redis目录 5.编译安装 ...

  6. 【转】Visual Studio Code(VS code)你们都在用吗?或许你们需要看一下这篇博文

    写在前面 在前端开发中,有一个非常好用的工具,Visual Studio Code,简称VS code. 都不用我安利VS code,大家就会乖乖的去用,无数个大言不惭的攻城狮,都被VS code比德 ...

  7. ASP.NET Core+MongoDB(一)

    项目类库:.Net Standar 2.0web:ASP.NET CORE 2.2 版本 先上图,看我们的解决方案结构: 分别对上面的工程进行说明:1.KYSharpCore:为公共的基础类,最底层 ...

  8. Java自学-集合框架 与数组的区别

    Java集合框架与数组的区别 示例 1 : 使用数组的局限性 如果要存放多个对象,可以使用数组,但是数组有局限性 比如 声明长度是10的数组 不用的数组就浪费了 超过10的个数,又放不下 //Test ...

  9. 前后端分离项目Vue+drf开发部署总结

    思维导图xmind文件:https://files-cdn.cnblogs.com/files/benjieming/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A ...

  10. Fiddler模拟响应

    1 启用规则 2 不匹配的规则通过,不勾选会导致不匹配的请求失败 3 匹配url 4 响应信息,Fiddler内置了一些响应脚步,你也可以选择一个保护响应信息的文本文件