【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 (题目链接) 题意 要求维护数列,操作有区间删除,区间插入,区间反转,区间修改,区间求和,求最 ...
随机推荐
- 最新数据库排行,Oracle略显疲惫
9月份TOPDB Top Database Index排行榜出炉,TOPDB Top Database Index是根据数据库在谷歌上的搜索频率分析得出的,数据库被搜索的频率越大,表示数 9月份的TO ...
- Unity3d 2017
Unity3d引擎的新纪元--Unity3d 2017 来源 http://blog.csdn.net/dark00800/article/details/75209544 Unity3d不久之前正式 ...
- nginx服务部署 说明
第1章 常用的软件 1.1 常用来提供静态服务的软件 Apache :这是中小型Web服务的主流,Web服务器中的老大哥, Nginx :大型网站Web服务的主流,曾经Web服务器中的初生牛犊 ...
- Spring AOP分析(1) -- 基本概念
AOP全称是Aspect Oriented Programming,面向切面编程,是面向对象编程(OOP:Object Oriented Programming)的补充和完善.一般在系统中,OOP利用 ...
- html 自定义标签使用实现方法
通过指定html命名空间的名字来定义自定义标签:默认的一些标签p div等都在html默认的命名空间下.而自定义的标签可以放在自定义的命名空间下,可通过xmlns:命名空间名 来指定,而自定义标签需要 ...
- http 状态
用户如果向您的服务器发出了某项请求要求显示您网站上的某个网页(例如,当用户通过浏览器访问您的网页或在 Googlebot 抓取该网页时),那么,您的服务器会返回 HTTP 状态代码以响应该请求.此状态 ...
- linux DHCP安装和测试
1.Yum 安装DHCP服务 2.拷贝模板配置文件,方便后期的配置修改. cp /usr/share/doc/dhcp-4.1.1/dhcpd.conf.sample /etc/dhcp/dhcpd. ...
- Java面试常会被问到的经典面试题,学习或者求职,你都要好好掌握
Java现在的热度虽然有所下降,但是,学Java的人依旧很多..Java的岗位也是渗透很多.那么,那些经典的Java知识点,你能看到问题就能说出一二三吗?来一起看看.. 1.JDK和JRE的区别 2. ...
- 【前端】跨浏览器事件处理程序EventUtil.js个人注释及详解
<javascript高级程序设计>跨浏览器事件处理程序EventUtil.js个人注释 EventUtil.js // 跨浏览器事件处理程序封装 var EventUtil = { // ...
- mkdir--命令
mkdir命令 mkdir:make directory(ies)的缩写,用来创建目录 1.语法 mkdir [OPTION]... DIRECTORY 注释:option是选择,可选,directo ...