本文将同步发布于:

题目

题目链接:gym102331H

题意概述

给定一个长度为 \(n\) 的序列 \(a\),有 \(q\) 次询问,每次询问给定三个参数 \(l,r,k\),求出对于区间 \([l,r]\),你将其划分为若干个子区间,然后取其中的 \(k\) 个,最大化取出来的所有元素的和。即:最大 \(k\) 子段和。

\(1\leq n,q\leq 3.5\times 10^4\),\(|a_i|\leq 3.5\times 10^4\)。

题解

寻找函数的性质

如果我们设 \(f_{[l,r]}(k)\) 表示区间 \(l,r\) 的最大 \(k\) 子段和,那么我们不难猜测到 \(l,r\) 相同时,\(f(k)\) 是一个上凸函数。

我们考虑证明这一点,即 \(f(k)-f(k-1)\geq f(k+1)-f(k)\)。

考虑反证法,设 \(\exists x\in\mathbb{N}\),满足 \(f(x)-f(x-1)<f(x+1)-f(x)\)。

那么我们考虑在 \(x-1,x,x+1\) 的时候的选取方案。

  • 若取 \(x+1\) 的时候正子段仍未取完,那么 \(f(x)\) 比 \(f(x-1)\) 多取了一个区间 \(p(p>0)\);\(f(x+1)\) 比 \(f(x)\) 多取了一个区间 \(q(q>0)\)。

    如果我们认为 \(f(x)-f(x-1)<f(x+1)-f(x)\),也就是 \(p<q\),那么我们不如交换这两个区间,可以使得 \(f(x)\) 更优。
  • 若取 \(x-1\) 的时候正子段取完了,那么 \(f(x)\) 比 \(f(x-1)\) 多取了一个区间 \(p(p<0)\);\(f(x+1)\) 比 \(f(x)\) 多取了一个区间 \(q(q<0)\)。

    如果我们认为 \(f(x)-f(x-1)<f(x+1)-f(x)\),也就是 \(p<q\),那么我们不如交换这两个区间,可以使得 \(f(x)\) 更优。

综上所述,如果选取的方案不满足上凸包的性质,我们总是可以通过调整法将其变成上凸包。

合并凸包——闵可夫斯基和

如果我们求出了区间 \([l,r]\) 内的 \(f(k)\),我们就想要知道这个东西是否支持快速合并,例如 \(f_{[l,\texttt{mid}]}+f_{[\texttt{mid}+1,r]}\to f_{[l,r]}\)。

答案是可以的。

考虑到 \(f(k)\) 的凸性,我们不妨使用 闵可夫斯基和 对两个凸包进行合并,时间复杂度为 \(\Theta(r-l)\)。

简单做法

通过上面的叙述,我们已经得到了一个简单的做法。

每次询问时,我们在线段树上求出此次询问覆盖的区间,并将所有的凸包合并,然后直接得到 \(f(k)\) 即为答案。

考虑分析时间复杂度,不难发现,这种做法的单次询问时间复杂度与区间长度有关,我们需要更优秀的做法。

wqs 二分

我们考虑不将区间合并,而是直接在线段树上的 \(\Theta(\log_2n)\) 个区间内求解答案。

具体地,我们决定使用 wqs 二分,解除掉选择区间个数的限制,然后各个区间就可以互不干扰的选择,很容易就能求出最优解。通过调整最终的斜率,我们可以得出答案,时间复杂度为 \(\Theta(q\log^3_2n)\)。

整体 wqs 二分

我们考虑到,当 \(k\) 增大时,其对应的 wqs 二分时的斜率也会越大,因此这个二分具有单调性,我们可以将询问对 \(k\) 排序,然后进行整体二分,常数更小的方法是在线段树上维护一个指针,表示上一次 \(k\leq x\) 的最优位置,然后暴力自增即可。

时间复杂度为 \(\Theta(q\log_2^2n)\)。

参考程序

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
static char buf[100000],*p1=buf,*p2=buf;
#define flush() (fwrite(wbuf,1,wp1,stdout),wp1=0)
#define putchar(c) (wp1==wp2&&(flush(),0),wbuf[wp1++]=c)
static char wbuf[1<<21];int wp1;const int wp2=1<<21;
inline int read(void){
reg bool f=false;
reg char ch=getchar();
reg int res=0;
while(!isdigit(ch))f|=(ch=='-'),ch=getchar();
while(isdigit(ch))res=10*res+(ch^'0'),ch=getchar();
return f?-res:res;
} inline void writeln(reg int x){
static char buf[32];
reg int p=-1;
if(x<0) x=-x,putchar('-');
if(!x) putchar('0');
else while(x) buf[++p]=(x%10)^'0',x/=10;
while(~p) putchar(buf[p--]);
putchar('\n');
return;
} inline int max(reg int a,reg int b){
return a>b?a:b;
} const int MAXN=5e4+5;
const int MAXQ=5e4+5;
const ll inf=1e12; struct querys{
int id,l,r,k,lef,rig;
}; inline bool cmp(const querys& a,const querys& b){
return (a.lef+a.rig)>(b.lef+b.rig);
} typedef vector<ll> Data; inline Data max(Data a,Data b){
if(a.size()>b.size()){
for(reg int i=0,siz=b.size();i<siz;++i)
a[i]=max(a[i],b[i]);
return a;
}
else{
for(reg int i=0,siz=a.size();i<siz;++i)
b[i]=max(b[i],a[i]);
return b;
}
} inline Data operator+(Data a,Data b){
if(!a.size()||!b.size())
return vector<ll>{};
Data res;
res.resize(a.size()+b.size()-1);
res[0]=a[0]+b[0];
reg unsigned int i=1,j=1,k=1;
while(i<a.size()&&j<b.size())
if(a[i]-a[i-1]>b[j]-b[j-1])
res[k++]=b[j-1]+a[i++];
else
res[k++]=a[i-1]+b[j++];
while(i<a.size())
res[k++]=b[j-1]+a[i++];
while(j<b.size())
res[k++]=a[i-1]+b[j++];
return res;
} inline Data shift(Data a){
if(!a.size())
return vector<ll>{};
else
return vector<ll>(a.begin()+1,a.end());
} inline void print(Data p){
for(auto x:p)
printf("%lld ",x);
return;
} int tim;
pair<ll,int> f[2],g[2]; namespace SegmentTree{
#define lson ( (k) << 1 )
#define rson ( (k) << 1 | 1 )
#define mid ( ( (l) + (r) ) >> 1 )
struct Node{
Data dat[2][2];
int t;
unsigned int ptr[2][2];
#define dat(x) unit[(x)].dat
#define t(x) unit[(x)].t
#define ptr(x) unit[(x)].ptr
};
Node unit[MAXN<<2];
inline void pushup(reg int k){
dat(k)[0][0]=max(
max(dat(lson)[0][0]+dat(rson)[0][0],dat(lson)[0][0]+dat(rson)[1][0]),
max(dat(lson)[0][1]+dat(rson)[0][0],max(dat(lson)[0][1]+dat(rson)[1][0],shift(dat(lson)[0][1]+dat(rson)[1][0])))
);
dat(k)[0][1]=max(
max(dat(lson)[0][0]+dat(rson)[0][1],dat(lson)[0][0]+dat(rson)[1][1]),
max(dat(lson)[0][1]+dat(rson)[0][1],max(dat(lson)[0][1]+dat(rson)[1][1],shift(dat(lson)[0][1]+dat(rson)[1][1])))
);
dat(k)[1][0]=max(
max(dat(lson)[1][0]+dat(rson)[0][0],dat(lson)[1][0]+dat(rson)[1][0]),
max(dat(lson)[1][1]+dat(rson)[0][0],max(dat(lson)[1][1]+dat(rson)[1][0],shift(dat(lson)[1][1]+dat(rson)[1][0])))
);
dat(k)[1][1]=max(
max(dat(lson)[1][0]+dat(rson)[0][1],dat(lson)[1][0]+dat(rson)[1][1]),
max(dat(lson)[1][1]+dat(rson)[0][1],max(dat(lson)[1][1]+dat(rson)[1][1],shift(dat(lson)[1][1]+dat(rson)[1][1])))
);
return;
}
inline void build(reg int k,reg int l,reg int r,reg int a[]){
if(l==r){
dat(k)[0][0]=vector<ll>{0,-inf},dat(k)[1][1]=vector<ll>{-inf,a[l]};
return;
}
build(lson,l,mid,a),build(rson,mid+1,r,a);
pushup(k);
return;
}
inline void query(reg int k,reg int l,reg int r,reg int L,reg int R,reg ll K){
if(t(k)!=tim){
t(k)=tim;
ptr(k)[0][0]=ptr(k)[0][1]=ptr(k)[1][0]=ptr(k)[1][1]=0;
}
if(L<=l&&r<=R){
g[0]=f[0],g[1]=f[1],f[0]=f[1]=make_pair(-inf,0);
for(reg int i=0;i<2;++i)
for(reg int j=0;j<2;++j)
if(dat(k)[i][j].size()){
while(ptr(k)[i][j]<dat(k)[i][j].size()-1&&dat(k)[i][j][ptr(k)[i][j]+1]-dat(k)[i][j][ptr(k)[i][j]]>=K)
++ptr(k)[i][j];
pair<ll,int> p=g[1],v;
if(i&&K>0)
p.first+=K,--p.second;
v=max(g[0],p);
v.first+=dat(k)[i][j][ptr(k)[i][j]]-ptr(k)[i][j]*K,v.second+=ptr(k)[i][j];
f[j]=max(f[j],v);
}
return;
}
if(L<=mid)
query(lson,l,mid,L,R,K);
if(R>mid)
query(rson,mid+1,r,L,R,K);
return;
}
#undef lson
#undef rson
#undef mid
#undef dat
#undef t
#undef ptr
} int n,q;
int a[MAXN];
querys qu[MAXQ],lef[MAXQ],rig[MAXQ];
int ans[MAXQ]; int main(void){
n=read(),q=read();
for(reg int i=1;i<=n;++i)
a[i]=read();
SegmentTree::build(1,1,n,a);
for(reg int i=1;i<=q;++i)
qu[i].id=i,qu[i].l=read(),qu[i].r=read(),qu[i].k=read(),qu[i].lef=-1e9,qu[i].rig=1e9;
while(true){
reg int cnt=0;
++tim;
sort(qu+1,qu+q+1,cmp);
for(reg int i=1;i<=q;++i)
if(qu[i].lef<=qu[i].rig){
++cnt;
f[0]=make_pair(0,0),f[1]=make_pair(-inf,0);
SegmentTree::query(1,1,n,qu[i].l,qu[i].r,(qu[i].lef+qu[i].rig)>>1);
pair<ll,int> res=max(f[0],f[1]);
if(res.second>=qu[i].k)
ans[qu[i].id]=res.first+qu[i].k*((qu[i].lef+qu[i].rig)>>1),qu[i].lef=((qu[i].lef+qu[i].rig)>>1)+1;
else
qu[i].rig=((qu[i].lef+qu[i].rig)>>1)-1;
}
if(!cnt)
break;
}
for(reg int i=1;i<=q;++i)
writeln(ans[i]);
flush();
return 0;
}

「题解」300iq Contest 2 H. Honorable Mention的更多相关文章

  1. 「题解」300iq Contest 2 B Bitwise Xor

    本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:gym102331B. 题意概述 给你一个长度为 \(n\) 的序列 \(a_i\),求一个最长的子序列满足所有子序列中的 ...

  2. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  3. 「题解」「HNOI2013」切糕

    文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...

  4. 「题解」JOIOI 王国

    「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...

  5. 「题解」:[loj2763][JOI2013]现代豪宅

    问题 A: 现代豪宅 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...

  6. 「题解」:$Six$

    问题 A: Six 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...

  7. 「题解」:$Smooth$

    问题 A: Smooth 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...

  8. 「题解」:Kill

    问题 A: Kill 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...

  9. 「题解」:y

    问题 B: y 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...

随机推荐

  1. CVE-2013-1347:Microsoft IE CGenericElement UAF 漏洞利用样本分析

    CVE-2013-1347 漏洞是典型的 IE 浏览器 UAF 漏洞,所以其利用方法和一般的 IE 浏览器漏洞的利用方法非常相似,所以流程大体上可以分为这些步骤:(1) 对象被释放 (2) 精确覆盖被 ...

  2. ThinkPHP5 Apache / IIs环境下 URL重写

    thinkPHP5新版本 隐藏index.php隐藏index.php 都写好了 public 隐藏 独立主机可以直接把根目录指向public下 虚拟主机可以把public下的index.php放到根 ...

  3. 用 shell 脚本制造连接频繁中断的场景

    问题的提出 最近在准备客户端的新版本,在内部灰度过程中,发现一类奇怪的 dump,通过查看日志和堆栈,可以确定是因为每次连上后台就被后台断开了.导致多次重连后随机发生的崩溃.dump 和日志都无法提供 ...

  4. Postman中如何实现接口之间的关联?

    Postman中如何实现接口之间的关联? 不单单说Postman中,我为什么拿Postman举例,因为它比较简单一点. 那如果我只问你如何实现接口之间的关联,那肯定有很多的方式,Postman只是其中 ...

  5. JavaWeb——JDBC

    内容索引 1. JDBC基本概念 2. 快速入门 3. 对JDBC中各个接口和类详解 JDBC: 1. 概念:Java DataBase Connectivity Java 数据库连接, Java语言 ...

  6. 【BUAA 软工热身作业】继往开来,勇攀高峰

    项目 内容 课程:2020春季软件工程课程博客作业(罗杰,任健) 博客园班级链接 作业:热身作业,阅读并撰写博客 作业要求 课程目标 学习大规模软件开发的技巧与方法,锻炼开发能力 作业目标 学习前辈经 ...

  7. OO_Unit1_表达式求导

    CSDN链接 一.第一次作业 1.需求分析 简单多项式导函数 带符号整数 支持前导0的带符号整数,符号可省略,如: +02.-16.19260817等. 幂函数 一般形式 由自变量x和指数组成,指数为 ...

  8. Linux_LVM管理

    一.Ivm的应用场景及其弊端 1.应用场景: 随着公司的发展,数据增长较快,最初规划的磁盘容量不够用了 2.弊端: 数据不是直接存放在硬盘上,而是在硬盘的.上面又虚拟出来--层逻辑卷存放数据,故而增加 ...

  9. Linux_配置加密的https

    一.配置https 1.安装好httpd服务后,安装mod_ssl模块 //首先查看是否安装mod_ssl [root@localhost ~]# rpm -qa | grep mod_ssl //安 ...

  10. Docker镜像的仓库及底层依赖的核心技术(3)

    一.docker镜像的仓库 仓库分为公共仓库和私有仓库 DockerHub的官方仓库:https://hub.docker.com DockerPool社区仓库:https://dl.dockerpo ...