NKOJ 1909 战场控制系统

问题描述

2050年,人类与外星人之间的战争已趋于白热化。就在这时,人类发明出一种超级武器,这种武器能够同时对相邻的多个目标进行攻击。凡是防御力小于或等于这种武器攻击力的外星人遭到它的攻击,就会被消灭。然而,拥有超级武器是远远不够的,人们还需要一个战地统计系统时刻反馈外星人部队的信息。这个艰巨的任务落在你的身上。请你尽快设计出这样一套系统。

这套系统需要具备能够处理如下2类信息的能力:

1.外星人向[x1,x2]内的每个位置增援一支防御力为v的部队。
2.人类使用超级武器对[x1,x2]内的所有位置进行一次攻击力为v的打击。系统需要返回在这次攻击中被消灭的外星人个数。

注:防御力为i的外星人部队由i个外星人组成,其中第j个外星人的防御力为j。

输入格式

第一行读入n,m。其中n表示有n个位置,m表示有m条信息。
以下有m行,每行有4个整数k,x1,x2,v用来描述一条信息 。k表示这条信息属于第k类。x1,x2,v为相应信息的参数。k=1 or 2。
注:你可以认为最初的所有位置都没有外星人存在。

规模

0< n<=1000;0 < x1 <= x2<=n;0 < v <= 1000;0 < m <= 2000

输出格式

按顺序输出需要返回的信息。


其实是线段树水题,但是因为对线段树的掌握还不是很好,错了很多次。

本题的规模相对其他线段树的题来说比较小,允许我们开1000个线段树维护每个防御力的外星人的分布情况。接下来的就是线段树的基本操作了。

以下为教训:

对于区间修改的题,最容易出问题的地方是lazy标记的合并。为了保证每次区间修改操作仍为O(logN)的复杂度,我们选用了打上lazy标记,在询问到时再处理的方式。如果当前节点下放lazy时,左右儿子节点也有lazy标记,那么下放时必须考虑lazy的合并。

对于lazy的合并,有绝对正确的做法:遇到左右儿子有lazy就递归处理,先处理了左右儿子的lazy下放,再处理当前节点的lazy。然而这样会使lazy退化,putdown函数失去了O(1)的时间复杂度优势我们必须尽可能保证putdown函数O(1)的时间复杂度(如:SCOI2010序列操作是线段树的模板题,但是如果没处理好各种类型lazy的合并就会WA)。

关于lazy的合并,记住以下两点:

  1. lazy的合并很多情况下不满足交换律,如本题“合并 父亲lazy=-1儿子lazy>0”和“合并 父亲lazy>0儿子lazy=-1””处理方式有所不同。
  2. 考虑lazy的合并时,除了当前节点和左右儿子,还要消去对儿子的儿子节点操作正确性造成的影响。这样其实已经有递归的意思,但是没有递归那么多的时间。

对于1,参照本题完整代码Putdown函数。

对于2,参照本题合并父亲lazy>0(整个区间加上某些数)与儿子lazy=-1(清空该区间)的情况:

对于下图,比较两段代码:

WA代码:

void Putdown(int p)
{int l=ls[p],r=rs[p];if(lazy[p]>0){if(lazy[l]==-1){lazy[l]=lazy[p];sum[l]=(b[l]-a[l]+1)*lazy[p];}else{lazy[l]+=lazy[p];sum[l]+=(b[l]-a[l]+1)*lazy[p];}……}……
}

AC代码:

void Putdown(int p)
{int l=ls[p],r=rs[p];if(lazy[p]>0){if(lazy[l]==-1){lazy[l]=lazy[p];sum[l]=(b[l]-a[l]+1)*lazy[p];//相比上面的代码,多了以下两行:lazy[ls[l]]=lazy[rs[l]]=-1;sum[ls[l]]=sum[rs[l]]=0;}else{lazy[l]+=lazy[p];sum[l]+=(b[l]-a[l]+1)*lazy[p];}……}……
}

可以看到,在下放1号节点的lazy值时,如果按照第一段代码,在之后下放2号节点时,lazy[3]将会被直接赋为3,而实际上,3号节点是再被清空一次之后再下放lazy=2,lazy本来应该为2。而第二段代码就很好地回避了这个问题。这就是上面说的“消去对儿子的儿子节点的影响”。

其实可以发现,多出的两行是等价于putdown(p)的,但是由于当lazy=-1时绝不会递归下去,仍然是O(1)的。在任何情况下都先下放儿子节点才会出现递归导致超时的问题。

非常改悔的完整代码:

#include<stdio.h>
#define MAXN 1005
#define MAXT 4000005
int N,M;int rt[MAXN],ls[MAXT],rs[MAXT],tot,lazy[MAXT],a[MAXT],b[MAXT],sum[MAXT];
void Build(int p,int x,int y)
{a[p]=x;b[p]=y;if(x==y)return;int mid=x+y>>1;ls[p]=++tot;Build(ls[p],x,mid);rs[p]=++tot;Build(rs[p],mid+1,y);
}void Update(int p){sum[p]=sum[ls[p]]+sum[rs[p]];}void Putdown(int p)
{int l=ls[p],r=rs[p];if(lazy[p]==-1){sum[l]=sum[r]=0;lazy[l]=lazy[r]=-1;}else{if(lazy[l]==-1){lazy[l]=lazy[p];lazy[ls[l]]=lazy[rs[l]]=-1;sum[ls[l]]=sum[rs[l]]=0;sum[l]=(b[l]-a[l]+1)*lazy[p];}else{lazy[l]+=lazy[p];sum[l]+=(b[l]-a[l]+1)*lazy[p];}if(lazy[r]==-1){lazy[r]=lazy[p];lazy[ls[r]]=lazy[rs[r]]=-1;sum[ls[r]]=sum[rs[r]]=0;sum[r]=(b[r]-a[r]+1)*lazy[p];}else{lazy[r]+=lazy[p];sum[r]+=(b[r]-a[r]+1)*lazy[p];}}lazy[p]=0;
}void Add(int p,int x,int y)
{if(b[p]<x||a[p]>y)return;if(lazy[p])Putdown(p);if(b[p]<=y&&a[p]>=x){lazy[p]++;sum[p]+=(b[p]-a[p]+1);return;}Add(ls[p],x,y);Add(rs[p],x,y);Update(p);
}int GetSum(int p,int x,int y)
{if(b[p]<x||a[p]>y)return 0;if(lazy[p])Putdown(p);int L=0,R=0;if(b[p]<=y&&a[p]>=x){L=sum[p];lazy[p]=-1;sum[p]=0;return L;}L=GetSum(ls[p],x,y);R=GetSum(rs[p],x,y);Update(p);return L+R;
}int main()
{int i,j,k,v,x,y,Ans=0;scanf("%d%d",&N,&M);for(i=1;i<=1000;i++)rt[i]=++tot,Build(rt[i],1,N);for(i=1;i<=M;i++){scanf("%d%d%d%d",&k,&x,&y,&v);if(k==1)for(j=1;j<=v;j++)Add(rt[j],x,y);else{Ans=0;for(j=1;j<=v;j++)Ans+=GetSum(rt[j],x,y);printf("%d\n",Ans);}}
}

战场控制系统 线段树相关推荐

  1. 二逼平衡树——树套树(线段树套Splay平衡树)

    题面 Bzoj3196 解析 线段树和Splay两棵树套在一起,常数直逼inf,但最终侥幸过了 思路还是比较简单, 在原数组维护一个下标线段树,再在每一个线段树节点,维护一个对应区间的权值Splay. ...

  2. 线段树——HDU - 1698

    题目含义 就是初始化一堆数为1 可以经过操作把一个区间的数都改变 并求这堆数的总大小 题目分析 有一个 #include<iostream> #include<stdio.h> ...

  3. BZOJ.1558.[JSOI2009]等差数列(线段树 差分)

    BZOJ 洛谷 首先可以把原序列\(A_i\)转化成差分序列\(B_i\)去做. 这样对于区间加一个等差数列\((l,r,a_0,d)\),就可以转化为\(B_{l-1}\)+=\(a_0\),\(B ...

  4. 【线段树分治 线性基】luoguP3733 [HAOI2017]八纵八横

    不知道为什么bzoj没有HAOI2017 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路 ...

  5. [bzoj1582][Usaco2009 Hol]Holiday Painting 节日画画_线段树

    Holiday Painting 节日画画 bzoj-1582 Usaco-2009 Hol 题目大意:给定两个n*m的01网格图.q次操作,每次将第二个网格图的子矩阵全部变成0或1,问每一次操作后两 ...

  6. codefores 786B. Legacy(最短路,线段树优化拆点,好题)

    题目链接 B. Legacy time limit per test2 seconds memory limit per test256 megabytes inputstandard input o ...

  7. 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树

    题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...

  8. 树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)

    题目链接 题意: 给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径(最短)上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一 ...

  9. bzoj1095: [ZJOI2007]Hide 捉迷藏 线段树维护括号序列 点分治 链分治

    这题真是十分难写啊 不管是点分治还是括号序列都有一堆细节.. 点分治:时空复杂度$O(n\log^2n)$,常数巨大 主要就是3个堆的初始状态 C堆:每个节点一个,为子树中的点到它父亲的距离的堆. B ...

最新文章

  1. dbcp2和dbcp 1.4在API层面的差异
  2. 拼音转汉字 和 字符编码测试
  3. jq定制上传按钮,选择文件就上传
  4. 编写测试:VC下获取文件大小的4种方法
  5. TypeScript里对数组元素的自定义属性排序的实现原理
  6. C++ 运算符重载规则
  7. Spring Boot参数校验
  8. uml学习之图书借阅简化用例图创建
  9. python35安装教程_python详细安装教程,非常值得看的一篇文章
  10. Silverlight 的文本框自动完成(AutoComplete)
  11. textbox控件输入内容后按下Enter键后执行button1的click方法
  12. Python+OpenCV:立体图像深度图(Depth Map from Stereo Images)
  13. unix操作系统设计高清中文
  14. Spring配置redis(自定义方法)
  15. ISP模块之RAW DATA去噪(二)--BM3D算法
  16. 移动通信网络规划:机房设计
  17. 怎么将路由器设置成交换机
  18. 【转载】手机快充的核心模块:ChargePump
  19. 2021-2027全球及中国远红外桑拿行业研究及十四五规划分析报告
  20. 故障描述:服务器应用程序不可用

热门文章

  1. LeetCode——LCP 29. 乐团站位[简单]——分析及代码(Java)
  2. TI C6000 DSP笔记
  3. 2019.12.19第一次发帖
  4. Ubuntu手动修改日期与时间
  5. 电磁继电器和固态继电器的区别
  6. 暑假博客二:由前序中序遍历生成二叉树
  7. 20KW三相三电平光伏逆变器 三相锁相 主控平台:TMS 20KW三相三电平光伏逆变器 三相锁相
  8. java 常用的数据库连接池,java通过数据库连接池的方式
  9. 计算机网络实验-交换机基本配置
  10. 小红伞Avira Premium Security Suite详细中文设置注释(转)