「题解」300iq Contest 2 H. Honorable Mention
本文将同步发布于:
题目
题目链接: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的更多相关文章
- 「题解」300iq Contest 2 B Bitwise Xor
本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:gym102331B. 题意概述 给你一个长度为 \(n\) 的序列 \(a_i\),求一个最长的子序列满足所有子序列中的 ...
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 「题解」「HNOI2013」切糕
文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...
- 「题解」JOIOI 王国
「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...
- 「题解」:[loj2763][JOI2013]现代豪宅
问题 A: 现代豪宅 时间限制: 1 Sec 内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...
- 「题解」:$Six$
问题 A: Six 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...
- 「题解」:$Smooth$
问题 A: Smooth 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...
- 「题解」:Kill
问题 A: Kill 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...
- 「题解」:y
问题 B: y 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...
随机推荐
- Win64 驱动内核编程-32.枚举与删除注册表回调
枚举与删除注册表回调 注册表回调是一个监控注册表读写的回调,它的效果非常明显,一个回调能实现在SSDT 上 HOOK 十几个 API 的效果.部分游戏保护还会在注册表回调上做功夫,监控 service ...
- 【spring源码系列】之【BeanDefinition】
1. BeanDefinition简介 前面讲的解析bean标签,本质就是将bean的信息封装成BeanDefinition对象的过程,最后放入容器beanDefinitionMap中.spring ...
- MySQL的Double Write并不难理解
目录 问题引入- 断电了 不一会 - 来电了 Double write工作流程 恢复的过程 配置参数 疑问 推荐阅读 问题引入- 断电了 今天为大家介绍一个新的名词:double write. 相信你 ...
- PHP 下载apk文件
方式一.public function downApkFile(){ $path = Env::get('root_path')."apk/"; //路径 $file_name = ...
- 远程连接mysql出现"Can't connect to MySQL server 'Ip' ()"的解决办法
1.大多是防火墙的问题(参考链接:https://blog.csdn.net/jiezhi2013/article/details/50603366) 2.上面方法不能解决,不造成影响情况下可关闭防火 ...
- Etcd中Raft日志复制的实现
Raft state of log commitIndex : A log entry is committed once the leader that created the entry has ...
- FROM-4-TO-6!!!!!!!!! - OO第二单元总结
电梯的这三次作业是对并发编程的一次管窥,感觉收获还是蛮多的.在设计上有好的地方也有不足,这里简单回顾总结一下 设计总述 电梯这个问题由于比较贴近真实生活,所以需求还是很好理解的.总的来说,我的数据处理 ...
- 使用基于centos7 dbus问题总结
Authorization not available. Check if polkit Authorization not available. Check if polkit service is ...
- [bug] flink on yarn 启动失败
参考 https://www.cnblogs.com/huangguoming/p/11732663.html
- [bug] python matplotlib 中文乱码
参考 matplotlib解决不显示中文问题 https://www.jianshu.com/p/b02ec7dc39dd