BZOJ.4695.最假女选手(线段树 Segment tree Beats!)
区间取\(\max,\ \min\)并维护区间和是普通线段树无法处理的。
对于操作二,维护区间最小值\(mn\)、最小值个数\(t\)、严格次小值\(se\)。
当\(mn\geq x\)时,不需要改变,return;\(se>x>mn\)时,\(sum=sum+(x-mn)*t\),打上区间\(\max\)标记;
当\(x\geq se>mn\)时,不会做,继续递归分别处理两个子区间,直到遇到前两种情况。
操作三同理,维护最大值、最大值个数、次大值。
复杂度\(O(m\log^2n)\),常表现为\(O(m\log n)\)。通常叫做吉司机线段树。
[Update]
如果可以建值域线段树就好写多了。。(然而这题是区间查询)
对于取\(\max/\min\)操作可以直接区间修改清空超出范围的值,然后更新到对应位置上就行了(比如对\(v\)取\(\max\),把\(\lt v\)的数全删掉,统计一下个数\(num\),然后在\(v\)处加上\(num\)个\(v\)即可)。
复杂度\(m\log n\),其实就是前/后缀的区间修改,也算和这题不太一样。
这样的题见BZOJ4399。
证明:(详见WC2016课件 Segment tree Beats!)


细节:
有两个修改(\(\max,\ \min\)),太恶心了。。
比如:取\(\min\)的时候不仅是改最大值,最小值也可能改(当然了...然而写这题的时候就是忘了)。
最大值改了\(\min\)标记也一定改(最大值是算了当前\(\min\)标记后的)。
还有\(\max\)标记也可能改,注意是取\(\min\)而不是直接赋值(还有加法影响这个标记,原先的最大值并不一定是由这个标记得到的)。
还有\(\min,\ \max\)可能会使得区间变为同一个数(第一句话的情况),这就需要特判然后把\(sum\),次小值,次大值初始化掉。
还有如果\(mn\)没有跟\(mx\)一起变为\(v\)(上一种情况),但是可能\(mn<v\geq se\),还要让次小值取个\(\min\)。
还有常数太大。。考虑把\(\min,\ \max\)标记去掉,直接在父节点更新,并在适合的时候下传:\(47s\to 40s\).
调到怀疑线段树。。感谢manchery dalao的代码。
https://blog.csdn.net/u014609452/article/details/54912473
吐槽:机房电脑都31s过,bzoj给我卡到47s。。虽然开了O2吧,但秀一波学校大机房配置:

bzoj加油。
标记优化:
//79280kb 40460ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=5e5+5,INF=2e9;
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now*f;
}
struct Segment_Tree
{
#define S N<<2
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int mn[S],smn[S],tmn[S],mx[S],smx[S],tmx[S],sz[S],add[S];
LL sum[S];
#undef S
inline void Update(int rt)
{
int l=ls,r=rs; sum[rt]=sum[l]+sum[r];
if(mn[l]<mn[r]) mn[rt]=mn[l],smn[rt]=std::min(smn[l],mn[r]),tmn[rt]=tmn[l];
else if(mn[l]>mn[r]) mn[rt]=mn[r],smn[rt]=std::min(smn[r],mn[l]),tmn[rt]=tmn[r];
else mn[rt]=mn[l],smn[rt]=std::min(smn[l],smn[r]),tmn[rt]=tmn[l]+tmn[r];
if(mx[l]>mx[r]) mx[rt]=mx[l],smx[rt]=std::max(smx[l],mx[r]),tmx[rt]=tmx[l];
else if(mx[l]<mx[r]) mx[rt]=mx[r],smx[rt]=std::max(smx[r],mx[l]),tmx[rt]=tmx[r];
else mx[rt]=mx[l],smx[rt]=std::max(smx[l],smx[r]),tmx[rt]=tmx[l]+tmx[r];
}
inline void Add(int x,int v)
{
add[x]+=v, mn[x]+=v, mx[x]+=v, sum[x]+=1ll*v*sz[x];
if(smn[x]!=INF) smn[x]+=v;
if(smx[x]!=-INF) smx[x]+=v;
}
inline void Min(int x,int v)
{
// if(v<mx[x])
sum[x]-=1ll*tmx[x]*(mx[x]-v);
mx[x]=v, mn[x]=std::min(mn[x],v);//!
if(mn[x]==mx[x]) sum[x]=1ll*sz[x]*v, tmn[x]=tmx[x]=sz[x], smn[x]=INF, smx[x]=-INF;//!
else smn[x]=std::min(smn[x],v);//!
}
inline void Max(int x,int v)
{
// if(v>mn[x])
sum[x]+=1ll*tmn[x]*(v-mn[x]);
mn[x]=v, mx[x]=std::max(mx[x],v);
if(mn[x]==mx[x]) sum[x]=1ll*sz[x]*v, tmn[x]=tmx[x]=sz[x], smn[x]=INF, smx[x]=-INF;
else smx[x]=std::max(smx[x],v);
}
void PushDown(int rt)
{
int l=ls,r=rs;
if(add[rt]) Add(l,add[rt]), Add(r,add[rt]), add[rt]=0;
if(mx[l]>mx[rt] && mx[rt]>smx[l]) Min(l,mx[rt]);//下传之前的min
if(mx[r]>mx[rt] && mx[rt]>smx[r]) Min(r,mx[rt]);
if(mn[l]<mn[rt] && mn[rt]<smn[l]) Max(l,mn[rt]);//下传之前的max
if(mn[r]<mn[rt] && mn[rt]<smn[r]) Max(r,mn[rt]);
}
void Build(int l,int r,int rt)
{
sz[rt]=r-l+1;
if(l==r)
{
tmn[rt]=tmx[rt]=1;
sum[rt]=mn[rt]=mx[rt]=read(), smn[rt]=INF, smx[rt]=-INF;
return;
}
Build(l,l+r>>1,ls), Build((l+r>>1)+1,r,rs);
Update(rt);
}
void Modify_Add(int l,int r,int rt,int L,int R,int v)
{
if(L<=l && r<=R) {Add(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify_Add(lson,L,R,v);
if(m<R) Modify_Add(rson,L,R,v);
Update(rt);
}
void Modify_Max(int l,int r,int rt,int L,int R,int v)
{
if(mn[rt]>=v) return;
if(L<=l && r<=R && smn[rt]>v) {Max(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify_Max(lson,L,R,v);
if(m<R) Modify_Max(rson,L,R,v);
Update(rt);
}
void Modify_Min(int l,int r,int rt,int L,int R,int v)
{
if(mx[rt]<=v) return;
if(L<=l && r<=R && smx[rt]<v) {Min(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify_Min(lson,L,R,v);
if(m<R) Modify_Min(rson,L,R,v);
Update(rt);
}
int Query_Max(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return mx[rt];
PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::max(Query_Max(lson,L,R),Query_Max(rson,L,R));
else return Query_Max(lson,L,R);
else return Query_Max(rson,L,R);
}
int Query_Min(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return mn[rt];
PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::min(Query_Min(lson,L,R),Query_Min(rson,L,R));
else return Query_Min(lson,L,R);
else return Query_Min(rson,L,R);
}
LL Query_Sum(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return sum[rt];
PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return Query_Sum(lson,L,R)+Query_Sum(rson,L,R);
else return Query_Sum(lson,L,R);
else return Query_Sum(rson,L,R);
}
}T;
int main()
{
#define S 1,n,1
int n=read(); T.Build(S);
for(int m=read(),opt,l,r; m--; )
{
opt=read(),l=read(),r=read();
if(opt==1) T.Modify_Add(S,l,r,read());
else if(opt==2) T.Modify_Max(S,l,r,read());
else if(opt==3) T.Modify_Min(S,l,r,read());
else if(opt==4) printf("%lld\n",T.Query_Sum(S,l,r));
else if(opt==5) printf("%d\n",T.Query_Max(S,l,r));
else printf("%d\n",T.Query_Min(S,l,r));
}
#undef S
return 0;
}
无优化:(靠fread卡过)
//94908kb 47472ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=5e5+5,INF=2e9;
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now*f;
}
struct Segment_Tree
{
#define S N<<2
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int mn[S],smn[S],tmn[S],mx[S],smx[S],tmx[S],sz[S],add[S],tagmn[S],tagmx[S];
LL sum[S];
#undef S
inline void Update(int rt)
{
int l=ls,r=rs; sum[rt]=sum[l]+sum[r];
if(mn[l]<mn[r]) mn[rt]=mn[l],smn[rt]=std::min(smn[l],mn[r]),tmn[rt]=tmn[l];
else if(mn[l]>mn[r]) mn[rt]=mn[r],smn[rt]=std::min(smn[r],mn[l]),tmn[rt]=tmn[r];
else mn[rt]=mn[l],smn[rt]=std::min(smn[l],smn[r]),tmn[rt]=tmn[l]+tmn[r];
if(mx[l]>mx[r]) mx[rt]=mx[l],smx[rt]=std::max(smx[l],mx[r]),tmx[rt]=tmx[l];
else if(mx[l]<mx[r]) mx[rt]=mx[r],smx[rt]=std::max(smx[r],mx[l]),tmx[rt]=tmx[r];
else mx[rt]=mx[l],smx[rt]=std::max(smx[l],smx[r]),tmx[rt]=tmx[l]+tmx[r];
}
inline void Add(int x,int v)
{
add[x]+=v, mn[x]+=v, mx[x]+=v, sum[x]+=1ll*v*sz[x];
if(smn[x]!=INF) smn[x]+=v;
if(smx[x]!=-INF) smx[x]+=v;
if(tagmn[x]!=INF) tagmn[x]+=v;
if(tagmx[x]!=-INF) tagmx[x]+=v;
}
inline void Min(int x,int v)
{
if(v<mx[x])
{
sum[x]-=1ll*tmx[x]*(mx[x]-v);
mx[x]=v, mn[x]=std::min(mn[x],v);//!
tagmn[x]=v/*! 首先要比最大值小才可能(且一定会)更新min标记*/,
tagmx[x]=std::min(tagmx[x],v);//!
if(mn[x]==mx[x]) sum[x]=1ll*sz[x]*v, tmn[x]=tmx[x]=sz[x], smn[x]=INF, smx[x]=-INF;//!
else smn[x]=std::min(smn[x],v);//!
}
}
inline void Max(int x,int v)
{
if(v>mn[x])
{
sum[x]+=1ll*tmn[x]*(v-mn[x]);
mn[x]=v, mx[x]=std::max(mx[x],v);
tagmx[x]=v, tagmn[x]=std::max(tagmn[x],v);
if(mn[x]==mx[x]) sum[x]=1ll*sz[x]*v, tmn[x]=tmx[x]=sz[x], smn[x]=INF, smx[x]=-INF;
else smx[x]=std::max(smx[x],v);
}
}
void PushDown(int rt)
{
if(add[rt]) Add(ls,add[rt]), Add(rs,add[rt]), add[rt]=0;
if(tagmn[rt]!=INF) Min(ls,tagmn[rt]), Min(rs,tagmn[rt]), tagmn[rt]=INF;
if(tagmx[rt]!=-INF) Max(ls,tagmx[rt]), Max(rs,tagmx[rt]), tagmx[rt]=-INF;
}
void Build(int l,int r,int rt)
{
sz[rt]=r-l+1, tagmn[rt]=INF, tagmx[rt]=-INF;
if(l==r)
{
tmn[rt]=tmx[rt]=1;
sum[rt]=mn[rt]=mx[rt]=read(), smn[rt]=INF, smx[rt]=-INF;
return;
}
Build(l,l+r>>1,ls), Build((l+r>>1)+1,r,rs);
Update(rt);
}
void Modify_Add(int l,int r,int rt,int L,int R,int v)
{
if(L<=l && r<=R) {Add(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify_Add(lson,L,R,v);
if(m<R) Modify_Add(rson,L,R,v);
Update(rt);
}
void Modify_Max(int l,int r,int rt,int L,int R,int v)
{
if(mn[rt]>=v) return;
if(L<=l && r<=R && smn[rt]>v) {Max(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify_Max(lson,L,R,v);
if(m<R) Modify_Max(rson,L,R,v);
Update(rt);
}
void Modify_Min(int l,int r,int rt,int L,int R,int v)
{
if(mx[rt]<=v) return;
if(L<=l && r<=R && smx[rt]<v) {Min(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify_Min(lson,L,R,v);
if(m<R) Modify_Min(rson,L,R,v);
Update(rt);
}
int Query_Max(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return mx[rt];
PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::max(Query_Max(lson,L,R),Query_Max(rson,L,R));
else return Query_Max(lson,L,R);
else return Query_Max(rson,L,R);
}
int Query_Min(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return mn[rt];
PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return std::min(Query_Min(lson,L,R),Query_Min(rson,L,R));
else return Query_Min(lson,L,R);
else return Query_Min(rson,L,R);
}
LL Query_Sum(int l,int r,int rt,int L,int R)
{
if(L<=l && r<=R) return sum[rt];
PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return Query_Sum(lson,L,R)+Query_Sum(rson,L,R);
else return Query_Sum(lson,L,R);
else return Query_Sum(rson,L,R);
}
}T;
int main()
{
#define S 1,n,1
int n=read(); T.Build(S);
for(int m=read(),opt,l,r; m--; )
{
opt=read(),l=read(),r=read();
if(opt==1) T.Modify_Add(S,l,r,read());
else if(opt==2) T.Modify_Max(S,l,r,read());
else if(opt==3) T.Modify_Min(S,l,r,read());
else if(opt==4) printf("%lld\n",T.Query_Sum(S,l,r));
else if(opt==5) printf("%d\n",T.Query_Max(S,l,r));
else printf("%d\n",T.Query_Min(S,l,r));
}
#undef S
return 0;
}
BZOJ.4695.最假女选手(线段树 Segment tree Beats!)的更多相关文章
- BZOJ 4695 最假女选手 线段树
题意: 给定一个长度为 N序列,编号从1 到 N.要求支持下面几种操作: 1.给一个区间[L,R] 加上一个数x 2.把一个区间[L,R] 里小于x 的数变成x 3.把一个区间[L,R] 里大于x ...
- bzoj 4695 最假女选手 吉利线段树
最假女选手 Time Limit: 50 Sec Memory Limit: 128 MBSubmit: 480 Solved: 118[Submit][Status][Discuss] Desc ...
- 【bzoj4695】最假女选手 线段树区间最值操作
题目描述 给定一个长度为 N 序列,编号从 1 到 N .要求支持下面几种操作:1.给一个区间[L,R] 加上一个数x 2.把一个区间[L,R] 里小于x 的数变成x 3.把一个区间[L,R] 里大于 ...
- bzoj 4695: 最假女选手 && Gorgeous Sequence HDU - 5306 && (bzoj5312 冒险 || 小B的序列) && bzoj4355: Play with sequence
算导: 核算法 给每种操作一个摊还代价(是手工定义的),给数据结构中某些东西一个“信用”值(不是手动定义的,是被动产生的),摊还代价等于实际代价+信用变化量. 当实际代价小于摊还代价时,增加等于差额的 ...
- bzoj 4695: 最假女选手
……一道丧病线段树膜板题…… 被常数卡的死去活来……QAQ 学到了些奇技淫巧:把取min标记 和 区间最小值 合并 可以快很多…… #include <bits/stdc++.h> #de ...
- 『线段树 Segment Tree』
更新了基础部分 更新了\(lazytag\)标记的讲解 线段树 Segment Tree 今天来讲一下经典的线段树. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间 ...
- 线段树(Segment Tree)(转)
原文链接:线段树(Segment Tree) 1.概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lg ...
- 线段树(segment tree)
线段树在一些acm题目中经常见到,这种数据结构主要应用在计算几何和地理信息系统中.下图就为一个线段树: (PS:可能你见过线段树的不同表示方式,但是都大同小异,根据自己的需要来建就行.) 1.线段树基 ...
- 【数据结构系列】线段树(Segment Tree)
一.线段树的定义 线段树,又名区间树,是一种二叉搜索树. 那么问题来了,啥是二叉搜索树呢? 对于一棵二叉树,若满足: ①它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 ②若它的右子树不空, ...
随机推荐
- 解决linux mysql命令 bash: mysql: command not found 的方法
错误: root@DB-02 ~]# mysql -u root-bash: mysql: command not found 原因:这是由于系统默认会查找/usr/bin下的命令,如果这个命令不在这 ...
- bzoj千题计划187:bzoj1770: [Usaco2009 Nov]lights 燈 (高斯消元解异或方程组+枚举自由元)
http://www.lydsy.com/JudgeOnline/problem.php?id=1770 a[i][j] 表示i对j有影响 高斯消元解异或方程组 然后dfs枚举自由元确定最优解 #in ...
- [转载]Supporting OData $inlinecount with the new Web API OData preview package
http://www.strathweb.com/2012/08/supporting-odata-inlinecount-with-the-new-web-api-odata-preview-pac ...
- Zookeeper集群 + Kafka集群 + KafkaOffsetMonitor 监控
一.Zookeeper ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一 ...
- RabbitMQ消费端消息的获取方式(.Net Core)
1[短链接]:BasicGet(String queue, Boolean autoAck) 通过request的方式独自去获取消息,断开式,一次次获取,如果返回null,则说明队列中没有消息. 隐患 ...
- Paint Fence
There is a fence with n posts, each post can be painted with one of the k colors.You have to paint a ...
- RESTful 个人理解总结【转】
转自:http://www.cnblogs.com/wang-yaz/p/9237981.html 一.什么是RESTful 面向资源 简单的说:RESTful是一种架构的规范与约束.原则,符合这种规 ...
- kali linux2.0安装vega
1.到官网下载安装包(选择版本:我这里以Linux64位为例) vega下载地址:https://subgraph.com/vega/download/ 2.解压到指定目录: unzip VegaBu ...
- 前后端分离之mockjs基本介绍
安装与使用 # 安装 npm install mockjs #使用 Mock var Mock = require('mockjs') var data = Mock.mock({ // 属性 lis ...
- PHP时间戳和日期转换
获取当前时间 <?php var_dump(time()); //获取当前时间戳 int(1502245603) 时间戳转换为时间,可以用date(‘Y-m-s h:i:s’, 具体时间戳来实现 ...