维护直线的线段树:Bzoj1568,Bzoj3938(Uoj88)
有这样一类线段树,可以维护一些直线方程并对每个点求出最大值。
首先先看BZOJ1568,输入给你条直线的方程,你需要对于指定的位置求出最大的函数值。
看到数据范围nlog^2n可做,考虑用线段树去维护。对于每个区间,怎么维护这个区间最高的直线呢?
显然,对于这个区间而言,最高的直线在各个位置都可能是不同的,看起来不是很可做。但是,我们能对于每个长度为1的区间维护最高的直线。
然而这样需要把修改push到底,时间复杂度为线性,所以应该怎么办呢?
考虑标记永久化,对于每个区间,维护其中点最高的直线。然后查询时对于跨越的多个区间的答案取max,这样可以完成查询。
怎么修改呢?考虑在某个区间上有两条直线(一条原来的,一条新加的),如果一条在整个区间都在另一条之上,那么可以把下面的那条线扔掉,直接返回。否则把中点较高的那一条留在这个区间,另外一条向(这条线)比留下的更高的一个子区间下放。
考虑这样为什么是对的,因为如果一条线在中点比另一条高,那么在某一边区间这条线一定完全“碾压”另一条线,另外一条是没有用的。所以只把另外一条在另一个子区间下放即可(自己画图或脑补一下就明白了)。
这个东西的复杂度是log^2n,因为最多修改logn个区间,对于每个区间最多下放logn层。
然后问题就解决了。
上代码:
BZOJ1568:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define debug cout
using namespace std;
const int maxn=5e4+1e2; int l[maxn<<],r[maxn<<],lson[maxn<<],rson[maxn<<];
int mid[maxn<<];
double k[maxn<<],b[maxn<<];
int n,m,cnt; inline double f(const double &k,const double &b,const int &x) {
return k * ( x - ) + b;
}
inline void build(int pos,int ll,int rr) {
l[pos] = ll , r[pos] = rr;
if( ll == rr )
return;
mid[pos] = ( ll + rr ) >> ;
build(lson[pos]=++cnt,ll,mid[pos]);
build(rson[pos]=++cnt,mid[pos]+,rr);
}
inline void update(int pos,double kk,double bb) {
if( f(kk,bb,l[pos]) <= f(k[pos],b[pos],l[pos]) && f(kk,bb,r[pos]) <= f(k[pos],b[pos],r[pos]) )
return;
if( l[pos] == r[pos] ) {
k[pos] = kk , b[pos] = bb;
return;
}
if( kk > k[pos] ) {
if( f(kk,bb,mid[pos]) > f(k[pos],b[pos],mid[pos]) ) {
update(lson[pos],k[pos],b[pos]);
k[pos] = kk , b[pos] = bb;
}
else
update(rson[pos],kk,bb);
}
else {
if( f(kk,bb,mid[pos]) > f(k[pos],b[pos],mid[pos]) ) {
update(rson[pos],k[pos],b[pos]);
k[pos] = kk , b[pos] = bb;
}
else
update(lson[pos],kk,bb);
}
}
inline double query(int pos,int tar) {
if( l[pos] == r[pos] )
return f(k[pos],b[pos],tar);
if( tar <= mid[pos] )
return max( f(k[pos],b[pos],tar) , query(lson[pos],tar) );
else if( tar > mid[pos] )
return max( f(k[pos],b[pos],tar) , query(rson[pos],tar) );
} int main() {
static char com[];
static double qq,kk,bb;
scanf("%d",&m) , n = 5e4;
build(cnt=,,n);
for(int i=,p;i<=m;i++) {
scanf("%s",com);
if( *com == 'Q' ) {
scanf("%d",&p);
qq = query(,p);
printf("%d\n",((int)qq)/);
}
else {
scanf("%lf%lf",&bb,&kk);
update(,kk,bb);
}
}
return ;
}
UOJ88:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define lli long long int
#define debug cout
using namespace std;
const int maxn=1e5+1e2,maxq=6e5+1e2; lli srt[maxq]; inline lli f(const lli &k,const lli &b,const int &x) {
return k * srt[x] + b;
} int lson[maxq<<],rson[maxq<<];
struct SegmentTree {
int cnt;
lli k[maxq<<],b[maxq<<]; inline void build(int pos,int ll,int rr) {
if( ll == rr )
return;
const int mid = ( ll + rr ) >> ;
build(lson[pos]=++cnt,ll,mid);
build(rson[pos]=++cnt,mid+,rr);
}
inline void update(int pos,int l,int r,int ll,int rr,lli kk,lli bb) {
if( rr < l || r < ll )
return;
const int mid = ( l + r ) >> ;
if( ll <= l && r <= rr ) {
if( f(kk,bb,l) <= f(k[pos],b[pos],l) && f(kk,bb,r) <= f(k[pos],b[pos],r) )
return;
if( l == r ) {
k[pos] = kk , b[pos] = bb;
return;
}
if( kk > k[pos] ) {
if( f(kk,bb,mid) > f(k[pos],b[pos],mid) ) {
update(lson[pos],l,mid,l,r,k[pos],b[pos]);
k[pos] = kk , b[pos] = bb;
}
else
update(rson[pos],mid+,r,ll,rr,kk,bb);
}
else {
if( f(kk,bb,mid) > f(k[pos],b[pos],mid) ) {
update(rson[pos],mid+,r,l,r,k[pos],b[pos]);
k[pos] = kk , b[pos] = bb;
}
else
update(lson[pos],l,mid,ll,rr,kk,bb);
}
}
update(lson[pos],l,mid,ll,rr,kk,bb);
update(rson[pos],mid+,r,ll,rr,kk,bb);
}
inline lli query(int pos,int l,int r,int tar) {
lli ret = f(k[pos],b[pos],tar);
if( l == r )
return ret;
const int mid = ( l + r ) >> ;
if( tar <= mid )
ret = max( ret , query(lson[pos],l,mid,tar) );
else
ret = max( ret , query(rson[pos],mid+,r,tar) );
return ret;
}
}up,down; int s[maxn],st[maxq],ed[maxq];
lli k[maxq],b[maxq];
int ope[maxq][];
int n,m,len=,cnt;
int v[maxn]; inline lli getb(int pos,int tme,lli newk) {
lli alpha = f(k[pos],b[pos],tme);
return alpha - newk * srt[tme];
}
inline void add(int pos,int tt,lli kk,lli bb=) {
k[++cnt] = kk;
if( !bb )
bb = getb(s[pos],tt,kk);
b[cnt] = bb;
st[cnt] = tt , ed[cnt] = len;
if( s[pos] )
ed[s[pos]] = tt;
s[pos] = cnt;
} inline void init() {
sort(srt+,srt++len);
len = unique(srt+,srt++len) - srt - ;
for(int i=;i<=m;i++)
ope[i][] = lower_bound(srt+,srt++len,ope[i][]) - srt;
for(int i=;i<=n;i++)
add(i,,,v[i]);
for(int i=;i<=m;i++)
if( *ope[i] ) {
add(ope[i][],ope[i][],ope[i][]);
} up.build(up.cnt=,,len); for(int i=;i<=cnt;i++) {
up.update(,,len,st[i],ed[i],k[i],b[i]);
down.update(,,len,st[i],ed[i],-k[i],-b[i]);
}
} inline void work() {
static lli ans;
for(int i=;i<=m;i++)
if( ! *ope[i] ) {
ans = max( up.query(,,len,ope[i][]) , down.query(,,len,ope[i][]) );
printf("%lld\n",ans);
}
} int main() {
static char com[];
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
scanf("%d",v+i);
for(int i=;i<=m;i++) {
scanf("%d",&ope[i][]);
srt[++len] = ope[i][];
scanf("%s",com);
if( *com == 'c' ) {
ope[i][] = ;
scanf("%d%d",&ope[i][],&ope[i][]);
}
}
init();
work(); return ;
}
上面的代码是无法通过BZOJ3938的,因为少了一个return。
BZOJ3938:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define lli long long int
#define debug cout
using namespace std;
const int maxn=1e5+1e2,maxq=6e5+1e2; lli srt[maxq]; inline lli f(const lli &k,const lli &b,const int &x) {
return k * srt[x] + b;
} int lson[maxq<<],rson[maxq<<];
struct SegmentTree {
int cnt;
lli k[maxq<<],b[maxq<<]; inline void build(int pos,int ll,int rr) {
if( ll == rr )
return;
const int mid = ( ll + rr ) >> ;
build(lson[pos]=++cnt,ll,mid);
build(rson[pos]=++cnt,mid+,rr);
}
inline void update(int pos,int l,int r,int ll,int rr,lli kk,lli bb) {
if( rr < l || r < ll )
return;
const int mid = ( l + r ) >> ;
if( ll <= l && r <= rr ) {
if( f(kk,bb,l) <= f(k[pos],b[pos],l) && f(kk,bb,r) <= f(k[pos],b[pos],r) )
return;
if( l == r ) {
k[pos] = kk , b[pos] = bb;
return;
}
if( kk > k[pos] ) {
if( f(kk,bb,mid) > f(k[pos],b[pos],mid) ) {
update(lson[pos],l,mid,l,r,k[pos],b[pos]);
k[pos] = kk , b[pos] = bb;
}
else
update(rson[pos],mid+,r,ll,rr,kk,bb);
}
else {
if( f(kk,bb,mid) > f(k[pos],b[pos],mid) ) {
update(rson[pos],mid+,r,l,r,k[pos],b[pos]);
k[pos] = kk , b[pos] = bb;
}
else
update(lson[pos],l,mid,ll,rr,kk,bb);
}
return;
}
update(lson[pos],l,mid,ll,rr,kk,bb);
update(rson[pos],mid+,r,ll,rr,kk,bb);
}
inline lli query(int pos,int l,int r,int tar) {
lli ret = f(k[pos],b[pos],tar);
if( l == r )
return ret;
const int mid = ( l + r ) >> ;
if( tar <= mid )
ret = max( ret , query(lson[pos],l,mid,tar) );
else
ret = max( ret , query(rson[pos],mid+,r,tar) );
return ret;
}
}up,down; int s[maxn],st[maxq],ed[maxq];
lli k[maxq],b[maxq];
int ope[maxq][];
int n,m,len=,cnt;
int v[maxn]; inline lli getb(int pos,int tme,lli newk) {
lli alpha = f(k[pos],b[pos],tme);
return alpha - newk * srt[tme];
}
inline void add(int pos,int tt,lli kk,lli bb=) {
k[++cnt] = kk;
if( !bb )
bb = getb(s[pos],tt,kk);
b[cnt] = bb;
st[cnt] = tt , ed[cnt] = len;
if( s[pos] )
ed[s[pos]] = tt;
s[pos] = cnt;
} inline void init() {
sort(srt+,srt++len);
len = unique(srt+,srt++len) - srt - ;
for(int i=;i<=m;i++)
ope[i][] = lower_bound(srt+,srt++len,ope[i][]) - srt;
for(int i=;i<=n;i++)
add(i,,,v[i]);
for(int i=;i<=m;i++)
if( *ope[i] ) {
add(ope[i][],ope[i][],ope[i][]);
} up.build(up.cnt=,,len); for(int i=;i<=cnt;i++) {
up.update(,,len,st[i],ed[i],k[i],b[i]);
down.update(,,len,st[i],ed[i],-k[i],-b[i]);
}
} inline void work() {
static lli ans;
for(int i=;i<=m;i++)
if( ! *ope[i] ) {
ans = max( up.query(,,len,ope[i][]) , down.query(,,len,ope[i][]) );
printf("%lld\n",ans);
}
} inline int getint() {
int ret = , fix = ;
char ch = getchar();
while( !isdigit(ch) )
fix = ch == '-' ? - : fix,
ch = getchar();
while( isdigit(ch) )
ret = ret * + ch - '',
ch = getchar();
return ret * fix;
}
int main() {
static char com[];
n = getint() , m = getint();
for(int i=;i<=n;i++)
v[i] = getint();
for(int i=;i<=m;i++) {
ope[i][] = getint();
srt[++len] = ope[i][];
scanf("%s",com);
if( *com == 'c' ) {
ope[i][] = ;
ope[i][] = getint() , ope[i][] = getint();
}
}
init();
work(); return ;
}
对了,今天我似乎达成成就:吓跑小学妹......话说我有那么凶神恶煞吗......
维护直线的线段树:Bzoj1568,Bzoj3938(Uoj88)的更多相关文章
- BZOJ_1798_[AHOI2009]维护序列_线段树
BZOJ_1798_[AHOI2009]维护序列_线段树 题意:老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: ( ...
- [AHOI 2009] 维护序列(线段树模板题)
1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec Memory Limit: 64 MB Description 老师交给小可可一个维护数列的任务,现在小 ...
- BZOJ 1798: [Ahoi2009]Seq 维护序列seq( 线段树 )
线段树.. 打个 mul , add 的标记就好了.. 这个速度好像还挺快的...( 相比我其他代码 = = ) 好像是#35.. ---------------------------------- ...
- CSU 1809 - Parenthesis - [前缀和+维护区间最小值][线段树/RMQ]
题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1809 Bobo has a balanced parenthesis sequenc ...
- 1798. [AHOI2009]维护序列【线段树】
Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2 ...
- bzoj 1798: [Ahoi2009]Seq 维护序列seq 线段树 区间乘法区间加法 区间求和
1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeO ...
- 【bzoj1798】[Ahoi2009]Seq 维护序列seq 线段树
题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一 ...
- 维护gcd的线段树 补发一波。。。
基础线段树(辣鸡的不行) 发现自己线段树除了会维护加法和乘法就啥也不会了QWQ太菜了 瞎写了一个维护gcd的 首先,gcd(x,y)= gcd(x,y-x) 并且很容易推广到n个数,所以我们可以把原数 ...
- Bzoj 1798: [Ahoi2009]Seq 维护序列seq(线段树区间操作)
1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec Memory Limit: 64 MB Description 老师交给小可可一个维护数列的任务,现在小可 ...
随机推荐
- 44、File类简介
使用File类创建文件夹 File类在java.io包下,看名字应该可以猜到,这个类是跟文件夹操作有关,下面使用File类中的方法在硬盘中创建文件夹. package com.sutaoyu.file ...
- 【译】第十篇 Integration Services:高级事件行为
本篇文章是Integration Services系列的第十篇,详细内容请参考原文. 简介在前一篇, we introduced fault tolerance by examining method ...
- Go net/http获取body中json格式数据
Go net/http获取body中json格式数据 package main import ( "encoding/json" "fmt" "io/ ...
- 突破XSS字符限制执行任意JS代码
突破XSS字符限制执行任意JS代码 一.综述 有些XSS漏洞由于字符数量有限制而没法有效的利用,只能弹出一个对话框来YY,本文主要讨论如何突破字符数量的限制进行有效的利用,这里对有效利用的定义是可以不 ...
- 一个diff工具,用于判断两个目录下所有的改动(比较新旧版本文件夹)
需求: 编写一个diff工具,用于判断两个目录下所有的改动 详细介绍: 有A和B两个目录,目录所在位置及层级均不确定 需要以B为基准找出两个目录中所有有改动的文件(文件或内容增加.修改.删除),将有改 ...
- verilog中wire与reg类型的区别
每次写verilog代码时都会考虑把一个变量是设置为wire类型还是reg类型,因此把网上找到的一些关于这方面的资料整理了一下,方便以后查找. wire表示直通,即只要输入有变化,输出马上无条件地反映 ...
- linux 命令之cut
cut是一个选取命令,就是将一段数据经过分析,取出我们想要的.一般来说,选取信息通常是针对“行”来进行分析的,并不是整篇信息分析的. (1)其语法格式为:cut [-bn] [file] 或 cut ...
- Oracle 函数 “把当前的用户(审核人,审核通过后)插入到数据表中”
create or replace function mcode_apply_update_personnel(p_mca_no VARCHAR2, -- 参数(实参) p_action VARCHA ...
- 简单计算器的C实现-函数指针,main函数传参
/** 程序功能:简单计算器,实现加减乘除平方* 作者版本日期:2015.11.08 zhouhb OK* 源代码:李明 <新概念C语言培训>第33集 C语言Shell命令解释器的实现* ...
- 程序设计分层思想和DAO设计模式的开发
无论是一个应用程序项目还是一个Web项目,我们都可以按照分层思想进行程序设计.对于分层,当下最流行划分方式是:表现层+控制层+业务层+数据层.其中,业务层和数据层被统称为后台业务层,而表现层和控制层属 ...