对于这样一类问题:

区间取min,区间求和。

N<=100000

要求O(nlogn)级别的算法

直观体会一下,区间取min,还要维护区间和

增加的长度很不好求。。。。

然鹅,

从前有一个来自杭州天水幼儿园的julao叫九条可怜

他发明了一个线段树的写法,

攻克了这个难题。

说起来很简单:

线段树维护区间最大值,区间严格次大值,和区间最大值出现次数

修改的时候,如果c大于mx,直接return

如果c小于mx而大于cmx,根据最大值的出现次数可以直接修改sum(注意必须是严格大于cmx,否则不能维护好严格次大值

如果c小于等于cmx,那么暴力递归左右儿子,最终会用前两个更新,回溯来pushup一下

复杂度?

前两个O(1)就回溯了,不管。

第三个操作貌似有些暴力?

由于只有取max,所以

假如开始有O(N)个不同的值,那么每进行一次第三次操作,至少mx,和cmx要变得一样。值域减少1

那么,第三次操作最多进行O(n)次,每次均摊O(logn)

所以复杂度O(nlogn)

例题(以及一些具体操作):

bzoj4695. 最假女选手

【bzoj4695】最假女选手

区间还要加?值域会改变,,,可以证明(就是说我不会证)复杂度是O(nlog^2n)

维护区间最大值,次大值,最大值出现次数,最小值同理。以及区间和,区间加标记

下放:

先下放区间加标记,现在儿子的情况大致和父亲一样了

区别在于,之前区间取min可能把最大值砍掉一些,但是没有在儿子中更新。

由于仅最大值小了一些,所以如果父亲的最大值在儿子的最大值和次大值之间,那么暴力再让儿子对父亲的最大值取个min(直接返回的,这个也是O(1)的)

第二种情况的更新时候:

可能造成最大值和最小值相同的情况,那么必然就是全部都相等了。特判一下,把次大值-inf,最大值inf(其实这个没有必要)

或者可能只有值域只有两个,那么次大值或者次小值也要尝试更新一下。其他值域的时候不影响。(这个必须有)

代码比较长:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define mid ((l+r)>>1)
#define ls t[x].lson
#define rs t[x].rson
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=6e5+;
const int inf=0x3f3f3f3f;
int n,m;
int a[N];
struct node{ int mx,cmx,tmx;
int mi,cmi,tmi;
ll sum;
ll ad;
int lson,rson;
}t[*N];
int tot;
void pushup(int x){
t[x].sum=t[ls].sum+t[rs].sum;
if(t[ls].mx>t[rs].mx){
t[x].mx=t[ls].mx,t[x].tmx=t[ls].tmx;t[x].cmx=max(t[ls].cmx,t[rs].mx);
}else if(t[ls].mx<t[rs].mx){
t[x].mx=t[rs].mx,t[x].tmx=t[rs].tmx;t[x].cmx=max(t[rs].cmx,t[ls].mx);
}else{
t[x].mx=t[ls].mx;t[x].tmx=t[ls].tmx+t[rs].tmx;t[x].cmx=max(t[rs].cmx,t[ls].cmx);
}
if(t[ls].mi<t[rs].mi){
t[x].mi=t[ls].mi,t[x].tmi=t[ls].tmi;t[x].cmi=min(t[ls].cmi,t[rs].mi);
}else if(t[ls].mi>t[rs].mi){
t[x].mi=t[rs].mi,t[x].tmi=t[rs].tmi;t[x].cmi=min(t[rs].cmi,t[ls].mi);
}else{
t[x].mi=t[ls].mi;t[x].tmi=t[ls].tmi+t[rs].tmi;t[x].cmi=min(t[rs].cmi,t[ls].cmi);
}
}
void addmax(int x,int l,int r,int c){//qu max
t[x].sum+=(ll)t[x].tmi*(c-t[x].mi);
t[x].mi=c;
t[x].mx=max(t[x].mx,c);
if(t[x].mi==t[x].mx){
t[x].sum=((ll)r-l+)*c;t[x].tmi=t[x].tmx=r-l+;t[x].cmi=inf;t[x].cmx=-inf;
}else t[x].cmx=max(t[x].cmx,c);
}
void addmin(int x,int l,int r,int c){
t[x].sum+=(ll)t[x].tmx*(c-t[x].mx);
t[x].mx=c;
t[x].mi=min(t[x].mi,c);
if(t[x].mi==t[x].mx){
t[x].sum=((ll)r-l+)*c;t[x].tmi=t[x].tmx=r-l+;t[x].cmi=inf;t[x].cmx=-inf;
}else t[x].cmi=min(t[x].cmi,c);
}
void build(int x,int l,int r){
if(l==r){
t[x].mx=t[x].mi=a[l];
t[x].cmx=-inf;t[x].cmi=inf;
t[x].tmx=t[x].tmi=;
t[x].sum=a[l];return;
}
ls=++tot;rs=++tot;
build(ls,l,mid);build(rs,mid+,r);
pushup(x);
}
void getsum(int x,int l,int r,int c){
t[x].ad+=c;t[x].sum+=(r-l+)*c;
t[x].mi+=c;t[x].cmi+=c;
t[x].mx+=c;t[x].cmx+=c;
}
void pushdown(int x,int l,int r){
if(t[x].ad){
getsum(ls,l,mid,t[x].ad);
getsum(rs,mid+,r,t[x].ad);
t[x].ad=;
}
if(t[ls].mx>t[x].mx&&t[ls].cmx<t[x].mx) addmin(ls,l,mid,t[x].mx);
if(t[rs].mx>t[x].mx&&t[rs].cmx<t[x].mx) addmin(rs,mid+,r,t[x].mx);
if(t[ls].mi<t[x].mi&&t[ls].cmi>t[x].mi) addmax(ls,l,mid,t[x].mi);
if(t[rs].mi<t[x].mi&&t[rs].cmi>t[x].mi) addmax(rs,mid+,r,t[x].mi);
}
void chanmx(int x,int l,int r,int L,int R,int c){
//cout<<x<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<" "<<c<<" "<<t[x].cmi<<endl;
if(L<=l&&r<=R){
if(t[x].mi>=c) return;
if(t[x].cmi>c) {
addmax(x,l,r,c);return;
}
pushdown(x,l,r);
chanmx(ls,l,mid,L,R,c);
chanmx(rs,mid+,r,L,R,c);
pushup(x);
return;
}
pushdown(x,l,r);
if(L<=mid) chanmx(ls,l,mid,L,R,c);
if(mid<R) chanmx(rs,mid+,r,L,R,c);
pushup(x);
}
void chanmi(int x,int l,int r,int L,int R,int c){
if(L<=l&&r<=R){
if(t[x].mx<=c) return;
if(t[x].cmx<c) {
addmin(x,l,r,c);return;
}
pushdown(x,l,r);
chanmi(ls,l,mid,L,R,c);
chanmi(rs,mid+,r,L,R,c);
pushup(x);
return;
}
pushdown(x,l,r);
if(L<=mid) chanmi(ls,l,mid,L,R,c);
if(mid<R) chanmi(rs,mid+,r,L,R,c);
pushup(x);
}
void add(int x,int l,int r,int L,int R,int c){
if(L<=l&&r<=R){
getsum(x,l,r,c);
return;
}
pushdown(x,l,r);
if(L<=mid) add(ls,l,mid,L,R,c);
if(mid<R) add(rs,mid+,r,L,R,c);
pushup(x);
}
int qmax(int x,int l,int r,int L,int R){
if(L<=l&&r<=R){
return t[x].mx;
}
pushdown(x,l,r);int ret=-inf;
if(L<=mid) ret=max(ret,qmax(ls,l,mid,L,R));
if(mid<R) ret=max(ret,qmax(rs,mid+,r,L,R));
return ret;
}
int qmin(int x,int l,int r,int L,int R){
if(L<=l&&r<=R){
return t[x].mi;
}
pushdown(x,l,r);int ret=inf;
if(L<=mid) ret=min(ret,qmin(ls,l,mid,L,R));
if(mid<R) ret=min(ret,qmin(rs,mid+,r,L,R));
return ret;
}
ll qsum(int x,int l,int r,int L,int R){
if(L<=l&&r<=R){
return t[x].sum;
}
pushdown(x,l,r);ll ret=;
if(L<=mid) ret+=qsum(ls,l,mid,L,R);
if(mid<R) ret+=qsum(rs,mid+,r,L,R);
return ret;
}
int main(){
rd(n);
for(reg i=;i<=n;++i){
rd(a[i]);
}
rd(m);
++tot;
build(,,n);
//cout<<" tot "<<tot<<endl;
int op,l,r,x;
int o=;
while(m--){
++o;
// cout<<" oooo "<<o<<endl;
rd(op);
switch(op){
case :rd(l);rd(r);rd(x);add(,,n,l,r,x);break;
case :rd(l);rd(r);rd(x);chanmx(,,n,l,r,x);break;
case :rd(l);rd(r);rd(x);chanmi(,,n,l,r,x);break;
case :rd(l);rd(r);printf("%lld\n",qsum(,,n,l,r));break;
case :rd(l);rd(r);printf("%d\n",qmax(,,n,l,r));break;
case :rd(l);rd(r);printf("%d\n",qmin(,,n,l,r));break;
}
}
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/12/27 9:57:30
*/

CF815D Karen and Cards

不亏是九老师自己出的题

[学习笔记]Segment Tree Beats!九老师线段树的更多相关文章

  1. [LintCode] Segment Tree Build II 建立线段树之二

    The structure of Segment Tree is a binary tree which each node has two attributes startand end denot ...

  2. Segment Tree Beats 区间最值问题

    Segment Tree Beats 区间最值问题 线段树一类特殊技巧! 引出:CF671C Ultimate Weirdness of an Array 其实是考试题,改题的时候并不会区间取最值,区 ...

  3. BZOJ.4695.最假女选手(线段树 Segment tree Beats!)

    题目链接 区间取\(\max,\ \min\)并维护区间和是普通线段树无法处理的. 对于操作二,维护区间最小值\(mn\).最小值个数\(t\).严格次小值\(se\). 当\(mn\geq x\)时 ...

  4. Segment tree Beats

    Segment tree Beats Segment tree Beats,吉司机线段树,主要是关于如何用线段树实现区间取min/max.我们先看一道例题: HDU5306 Gorgeous Sequ ...

  5. [BZOJ4695]最假女选手:segment tree beats!

    分析 segment tree beats!模板题. 看了gxz的博客突然发现自己写的mxbt和mnbt两个标记没用诶. 代码 #include <bits/stdc++.h> #defi ...

  6. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图 学习目标 理解为什么需要法线贴图: 学习法线贴图如 ...

  7. ACM学习历程——POJ3321 Apple Tree(搜索,线段树)

          Description There is an apple tree outside of kaka's house. Every autumn, a lot of apples will ...

  8. ACM学习历程——NOJ1113 Game I(贪心 || 线段树)

    Description 尼克发明了这样一个游戏:在一个坐标轴上,有一些圆,这些圆的圆心都在x轴上,现在给定一个x轴上的点,保证该点没有在这些圆内(以及圆上),尼克可以以这个点为圆心做任意大小的圆,他想 ...

  9. [学习笔记]dsu on a tree(如何远离线段树合并)

    https://www.zybuluo.com/ysner/note/1318613 背景 这玩意来源于一种有局限性的算法. 有一种广为人知的,树上离线维护子树信息的做法. (可以参照luogu360 ...

随机推荐

  1. pycharm 3.4 亲测可使用到2019年2月的注册码,要用者从速

    注册码: D87IQPUU3Q-eyJsaWNlbnNlSWQiOiJEODdJUVBVVTNRIiwibGljZW5zZWVOYW1lIjoiTnNzIEltIiwiYXNzaWduZWVOYW1l ...

  2. Web自动化测试环境搭建1(基于firefox火狐浏览器)

    自动化测试是时代趋势,因此很多测试人员开始研究自动化测试,web自动化测试化测试并不难,但是很多人都是被挡在了环境搭建这一步,后面学习激情全无,这里,韬哥手把手教大家搭建火狐浏览器下的自动化测试环境( ...

  3. Java应用基础微专业-入门篇

    第1章--用程序来做计算 1.1 第一个Java程序 Mac version: Preference -> General -> Keys -> Search "Conte ...

  4. TW实习日记:第26天

    这周组长休年假去了,并且之前主要负责的项目也已经上线了,可以说没那么忙了,手头就一个协助别的组做的移动端项目.可是这个项目特别坑,由于网端是9年前的项目,导致后台的接口有非常多的问题,并且入参多得令人 ...

  5. 【转】Bootstrap FileInput中文API整理

    Bootstrap FileInput中文API整理 这段时间做项目用到bootstrap fileinput插件上传文件,在用的过程中,网上能查到的api都不是很全,所以想着整理一份比较详细的文档, ...

  6. ubuntu samba配置注意事项

    1. 下载samba前, ubuntu镜像源需要更新为国内源,否则samba的安装会非常慢 亲测,清华的镜像源速度满足要求. A.登录 https://mirrors.tuna.tsinghua.ed ...

  7. Ubuntu 常用软件推荐(QQ、微信、MATLAB等)及安装过程

    1. Wine QQ QQ 移植到 Linux 一直是一个比较头疼的问题,但我们日常交流.传输文件又离不开这个软件.在网上一番搜寻尝试后,发现最好的替代方案就是 Wine QQ,版本也还比较新,缺点是 ...

  8. POJ 2986 A Triangle and a Circle(三角形和圆形求交)

    Description Given one triangle and one circle in the plane. Your task is to calculate the common are ...

  9. POJ 2229 计数DP

    dp[i]代表是数字i的最多组合数如果i是一个奇数,i的任意一个组合都包含1,所以dp[i] = dp[i-1] 如果i是一个偶数,分两种情况讨论,一种是序列中包含1,因此dp[i]=dp[i-1]一 ...

  10. lintcode-151-买卖股票的最佳时机 III

    151-买卖股票的最佳时机 III 假设你有一个数组,它的第i个元素是一支给定的股票在第i天的价格.设计一个算法来找到最大的利润.你最多可以完成两笔交易. 注意事项 你不可以同时参与多笔交易(你必须在 ...