【NOI2005】维护数列
https://daniu.luogu.org/problem/show?pid=2042
一道伸展树维护数列的很悲伤的题目,共要维护两个标记和两个数列信息,为了维护MAX-SUM还要维护从左端开始的数列的最大和及到右端结束的数列的最大和。
按照伸展树的套路,给数列左右两边加上不存在的边界节点,给每个子树的空儿子指向哨兵节点。
维护最大子数列和
题目说的子数列其实要求至少包含一个元素,这就要很恶心的维护方法。
(其实让max_sum可以不含元素也能过90%)
每个节点定义max_sum:该节点的最大数列和(至少包含一个元素)
max_lsum:该节点的从左端开始的最大数列和(可以不包含元素)
max_rsum:该节点的到右端结束的最大数列和(可以不包含元素)
按照分冶法,max_sum=max{左儿子max_sum,右儿子max_sum,左儿子max_rsum+该节点的值+右儿子max_lsum}。
如果它和它的左右儿子都是普通节点,这个转移保证至少有一个元素。
如果它是普通节点或边界节点,它的左或右儿子是哨兵节点,则左儿子max_sum或右儿子max_sum是不可取的。故令哨兵节点的max_sum=-inf。
如果它是边界节点,它必定至多有一个儿子,令它的max_sum等于它的唯一儿子的max_sum,max_lsum与max_rsum同理。
覆盖子数列和翻转子数列
每个节点定义两个标记replaced和reversed。
replaced:这个节点及它的所有后代都应该修改为一个特定的值,但实际上只有这个节点的值已经修改。
reversed:这个节点及它的所有后代都应该交换左右子树(max_lsum和max_rsum也应该跟着交换),但实际上只有这个节点的左右子树已经交换。
可见这两个标记是互斥的,且replaced标记的优先级显然大于reversed标记。
打标记的时候注意维护每个结点的标记至多有一个就可以了。
350行的不压行代码,6.33KB,调了近8小时交了差不多二十遍才AC:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
void getstr(string &s)
{
int c;
s = "";
while (!isalpha(c = getchar()))
{
if (c == EOF)
return;
}
do
s += (char)c;
while (isalpha(c = getchar()) || c == '-');
}
void getint(int &x)
{
int c;
bool flag = false;
x = ;
while (!isdigit(c = getchar()))
{
if (c == EOF)
return;
if (c == '-')
flag = true;
}
do
x = x * + c - '';
while (isdigit(c = getchar()));
if (flag)
x = -x;
}
namespace splay
{
const int inf = 0x7fffffff;
enum direction
{
l = ,
r
};
struct node;
node *nil = , *l_edge, *r_edge;
struct node
{
int val, size;
node *ch[];
int sum;
int max_sum, max_lsum, max_rsum;
// max_sum 定义为最少包含一个元素的最大子数列和
// max_lsum 定义为从左端开始的可以不包含元素的最大子数列和
// max_lsum 定义为到右端结束的可以不包含元素的最大子数列和 bool replaced, reversed;
// 当replaced为true,表示它的所有后代的val应该与这个节点的val相同,但实际上后代节点并没有更新
// 当reversed为true,表示它已经交换了左右节点和左右最大值,且它的所有后代都应该交换左右子树和左右最大值,但实际上后代节点并没有更新 node(int v) : val(v), size(), sum(v), replaced(false), reversed(false)
{
ch[l] = ch[r] = nil;
if (v >= )
max_sum = max_lsum = max_rsum = sum;
else
{
max_sum = v;
max_lsum = max_rsum = ;
}
}
int cmp(int k)
{
if (k == ch[l]->size + || this == nil)
return -;
else
return k <= ch[l]->size ? l : r;
} void reverse()
{
if (!replaced)
{
reversed ^= ;
swap(ch[l], ch[r]);
swap(max_lsum, max_rsum);
}
}
void replace(int v)
{
reversed = false;
replaced = true;
val = v;
sum = v * size;
if (v > )
max_sum = max_lsum = max_rsum = sum;
else
{
max_sum = v; // 由于子数列要求至少有一个元素,故当 val < 0
// ,只有一个元素时和最大
max_lsum = max_rsum = ;
}
} void push_down()
{
if (replaced)
{
ch[l]->replace(val);
ch[r]->replace(val);
replaced = false;
}
else if (reversed)
{
ch[l]->reverse();
ch[r]->reverse();
reversed = false;
}
}
void pull_up()
{
if (this != nil)
{
size = ch[l]->size + ch[r]->size + ; if (!replaced)
sum = ch[l]->sum + ch[r]->sum + val;
else
sum = val * size; if (this != l_edge && this != r_edge)
{
max_sum = max(ch[l]->max_rsum + val + ch[r]->max_lsum,
max(ch[l]->max_sum,
ch[r]->max_sum)); // 更新后 max_sum 至少包含一个元素
max_lsum = max(
ch[l]->max_lsum,
ch[l]->sum + val +
ch[r]->max_lsum); // 更新后 max_lsum / max_rsum 可以不包含元素
max_rsum = max(ch[r]->max_rsum, ch[l]->max_rsum + val + ch[r]->sum);
}
else if (this == l_edge) // 注意特判左右边界节点
{
// 若不特判,当左边界节点为根且整个数列的从左开始的最大值为0时
// 就会出现 max_sum = ch[l]->max_rsum + val + ch[r]->max_lsum
// 即 max_sum = 0,这显然不合法
max_sum = ch[r]->max_sum;
max_lsum = ch[r]->max_lsum;
max_rsum = ch[r]->max_rsum;
}
else
{
// 右边界同理
max_sum = ch[l]->max_sum;
max_lsum = ch[l]->max_lsum;
max_rsum = ch[l]->max_rsum;
}
}
} void remove()
{
if (this != nil)
{
ch[l]->remove();
ch[r]->remove();
delete this;
}
}
} * root;
void init()
{
if (!nil)
nil = new node();
nil->size = ;
nil->ch[l] = nil->ch[r] = nil;
nil->max_sum = -inf;
l_edge = new node(), r_edge = new node();
l_edge->max_sum = -inf;
r_edge->max_sum = -inf;
root = nil;
}
void rotate(node *&t, int d)
{
t->push_down();
t->ch[l]->push_down();
t->ch[r]->push_down();
node *k = t->ch[d ^ ];
t->ch[d ^ ] = k->ch[d];
k->ch[d] = t;
t->pull_up();
k->pull_up();
t = k;
}
void splay(node *&t, int k)
{
t->push_down();
int d = t->cmp(k);
if (d == r)
k = k - t->ch[l]->size - ;
if (d != -)
{
t->ch[d]->push_down();
int d2 = t->ch[d]->cmp(k);
int k2 = (d2 == r) ? k - t->ch[d]->ch[l]->size - : k;
if (d2 != -)
{
splay(t->ch[d]->ch[d2], k2);
if (d == d2)
{
rotate(t, d ^ );
rotate(t, d ^ );
}
else
{
rotate(t->ch[d], d2 ^ );
rotate(t, d ^ );
}
}
else
rotate(t, d ^ );
}
}
void join(node *&t1, node *&t2)
{
if (t1 == nil)
swap(t1, t2);
splay(t1, t1->size);
t1->ch[r] = t2;
t2 = nil;
t1->pull_up();
}
node *split(node *&t, int k)
{
if (k == )
{
node *subtree = t;
t = nil;
return subtree;
}
splay(t, k);
node *subtree = t->ch[r];
t->ch[r] = nil;
t->pull_up();
return subtree;
}
node *build_tree(int *p, int n)
{
if (n == )
return nil;
node *fa;
node *ch = new node(p[]);
for (int i = ; i <= n; i++)
{
fa = new node(p[i]);
fa->ch[l] = ch;
fa->pull_up();
ch = fa;
}
return fa;
}
node *select(int p, int tot)
{
int ln = p, rn = ln + tot - ;
splay(root, rn + );
splay(root->ch[l], ln - );
return root->ch[l]->ch[r];
}
}
int n, m;
int num[];
int main()
{
using namespace splay;
ios::sync_with_stdio(false);
getint(n);
getint(m);
for (int i = ; i <= n; i++)
getint(num[i]);
init(); node *t1, *t2; // tmp
root = l_edge;
t1 = build_tree(num, n);
join(root, t1);
t1 = r_edge;
join(root, t1); string opt;
int posi, tot, c;
while (m--)
{
getstr(opt);
switch (opt[])
{
case 'I': // INSERT
getint(posi);
getint(tot);
posi++;
for (int i = ; i <= tot; i++)
getint(num[i]);
t1 = build_tree(num, tot);
t2 = split(root, posi);
join(root, t1);
join(root, t2);
break;
case 'D': // DELETE
getint(posi);
getint(tot);
posi++;
t1 = split(root, posi - );
t2 = split(t1, tot);
join(root, t2);
t1->remove();
break;
case 'R': // REVERSE
getint(posi);
getint(tot);
posi++;
t1 = select(posi, tot);
t1->reverse();
root->ch[l]->pull_up();
root->pull_up();
break;
case 'G': // GET-SUM
getint(posi);
getint(tot);
posi++;
t1 = select(posi, tot);
cout << t1->sum << endl;
break;
case 'M':
if (opt[] == 'K') // MAKE_SAME
{
getint(posi);
getint(tot);
getint(c);
posi++;
t1 = select(posi, tot);
t1->replace(c);
root->ch[l]->pull_up();
root->pull_up();
}
else // MAX_SUM
cout << root->max_sum << endl;
break;
}
}
return ;
}
【NOI2005】维护数列的更多相关文章
- 数据结构(Splay平衡树):COGS 339. [NOI2005] 维护数列
339. [NOI2005] 维护数列 时间限制:3 s 内存限制:256 MB [问题描述] 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际 ...
- [NOI2005] 维护数列
[NOI2005] 维护数列 题目 传送门 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格) 操作编号 输入文件中的格式 说明 1 ...
- P2042 [NOI2005]维护数列 && Splay区间操作(四)
到这里 \(A\) 了这题, \(Splay\) 就能算入好门了吧. 今天是个特殊的日子, \(NOI\) 出成绩, 大佬 \(Cu\) 不敢相信这一切这么快, 一下子机房就只剩我和 \(zrs\) ...
- 洛谷 P2042 [NOI2005]维护数列-Splay(插入 删除 修改 翻转 求和 最大的子序列)
因为要讲座,随便写一下,等讲完有时间好好写一篇splay的博客. 先直接上题目然后贴代码,具体讲解都写代码里了. 参考的博客等的链接都贴代码里了,有空再好好写. P2042 [NOI2005]维护数列 ...
- P2042 [NOI2005]维护数列[splay或非旋treap·毒瘤题]
P2042 [NOI2005]维护数列 数列区间和,最大子列和(必须不为空),支持翻转.修改值.插入删除. 练码力的题,很毒瘤.个人因为太菜了,对splay极其生疏,犯了大量错误,在此记录,望以后一定 ...
- Luogu P2042 [NOI2005]维护数列(平衡树)
P2042 [NOI2005]维护数列 题意 题目描述 请写一个程序,要求维护一个数列,支持以下\(6\)种操作:(请注意,格式栏中的下划线'_'表示实际输入文件中的空格) 输入输出格式 输入格式: ...
- [NOI2005]维护数列(区间splay)
[NOI2005]维护数列(luogu) 打这玩意儿真是要了我的老命 Description 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文 ...
- [转载]无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
转自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182631.html 1500: [NOI2005]维修数列 Time Limit: 10 Sec Mem ...
- [您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MB Description Input 输入的第1 行包含两个数N 和M(M ≤20 ...
- 【bzoj1500】 noi2005—维护数列
http://www.lydsy.com/JudgeOnline/problem.php?id=1500 (题目链接) 题意 要求维护数列,操作有区间删除,区间插入,区间反转,区间修改,区间求和,求最 ...
随机推荐
- 爬取朋友圈,Get年度关键词
人生苦短,我用Python && C#. 1.引言 最近初学Python,写爬虫上瘾.爬了豆瓣练手,又爬了公司的论坛生成词云分析年度关键词.最近琢磨着2017又仅剩两月了,我的年度关键 ...
- python基础6 迭代器 生成器
可迭代的:内部含有__iter__方法的数据类型叫可迭代的,也叫迭代对象实现了迭代协议的对象 运用dir()方法来测试一个数据类型是不是可迭代的的. 迭代器协议是指:对象需要提供next方法,它要么返 ...
- AngularJS学习篇(十)
AngularJS Select(选择框) 使用 ng-options 创建选择框 在 AngularJS 中我们可以使用 ng-option 指令来创建一个下拉列表,列表项通过对象和数组循环输出,如 ...
- Flex 基础语法(三)
2.flex-wrap 默认情况下,项目都排在一条线(又称"轴线")上.flex-wrap属性定义,如果一条轴线排不下,如何换行 属性 含义 nowrap(默认值) 不换行 wra ...
- JQuery源码-------JQuery中数值型变量的判断isNumeric
判断一个数值型变量的方法,在jquery中非常简单,只有一行代码. isNumeric: function( obj ) { // parseFloat NaNs numeric-cast false ...
- 获取标签的src属性兼容性
获取节点如script标签的src属性时,针对非IE6,IE7可以直接使用src属性,但在IE6-7中存在问题,可以借助getAttribute方法 getAttribute(attr,iflag) ...
- [Bayes] Why we prefer Gaussian Distribution
最后还是选取一个朴素直接的名字,在此通过手算体会高斯的便捷和神奇. Ref: The Matrix Cookbook 注意,这里的所有变量默认都为多元变量,不是向量就是矩阵.多元高斯密度函数如下: 高 ...
- EF分页
先来看看几个LINQ to SQL的几个函数. Take 说明:获取集合的前n个元素:延迟.即只返回限定数量的结果集. var q = ( from e in db.Employees order ...
- JavaScript学习笔记(二)——字符串
在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...
- Celery 源码解析七:Worker 之间的交互
前面对于 Celery 的分布式处理已经做了一些介绍,例如第五章的 远程控制 和第六章的 Event机制,但是,我认为这些分布式都比较简单,并没有体现出多实例之间的协同作用,所以,今天就来点更加复杂的 ...