题目

给出一个长度为 \(n\) 的序列 \(A\) ;

你需要构造一个新的序列\(B\) ,满足:

  • $B_{i} \le B_{i+1} (1 \le i \lt n ) $
  • $\sum_{i=1}^{n} (A_i - B_i)^2 $ 最小

题解

  • 出题人和题解在这里 : http://15283746.blog.uoj.ac/blog/4966

  • 我只是整理了一下证明(Part 1)并套了一种做法(Part 2);

  • Part 1

  • 主要讨论最优的策略:

  • 引理一

    如果要求所有\(B_i\)相同等于\(x\),那么$x \ = \ \frac{\sum_{i=1}^{n} A_i }{n} $

    证明:写成关于\(x\)的二次函数即可证明;

  • 定理一

    ​ 答案一定是很多严格上升的等值段,即每一段的\(B\)相同,且等于这段\(A\)的平均值\(v(A)\) ;

    证明:由于相邻等值块的值严格不同,根据二次函数的性质,一定可以向靠近平均值的方向调整;

  • 引理二

    ​ 将任意一个序列延长之后,原序列对延长后的序列答案的贡献不小于原序列的最优答案;

    证明:由于是最优的所以显然,否则矛盾;

  • 引理三

    ​ 等值段的划分唯一;

    证明:

    ​ ( - _ -) 建立线性规划模型, $B_i \le B_{i+1} $ 这个不等式组在空间中的解是一个凸集,最优的点相当于和圆\((A_1,A_2,...,)\)的切点,感受一下可以知道是唯一的;

  • 定理二

    ​ 下列构造方法是最优的:

    ​ 维护一个以平均值为权值的单调栈,当加入一个点时,如果不满足单调性,则合并栈尾和当前区间,重复这个过程,最后将区间加入单调栈;

    证明:

    ​ 最优的划分为\(H[x,y]\) ,令如上策略的划分为\(H'[x,y]\) ,考虑归纳;

    ​ 假设对\(1 \le i \lt n\)均满足\(H=H'\) ,当\(i=n\)时:

    ​ 1.如果\(H'\)中含有两个及以上个等值块,可以划分两个长度<n的区间分别构造\(H_1'\)和\(H_2'\);

    ​ 根据引理二,可以知道一定是最优的;

    ​ 由构造方式这两个区间一定可以直接拼接且$H' = H_1' + H_2' $;

    ​ 2.如果\(H'\)中只有一个等值块,只需要证明任意大于1个等值块的\(H\)都不优即可;

    ​ 假设\(i\)是划分\(H\)的最后一个满足\(i\)和\(i+1\)不同块的点;

    ​ 考虑\(H'[1,i]\),设\(i\)在的块是\([j,i]\),在\(i+1\)到\(n\)之间一定存在第一个\(k\)合并了\([ j,i ]\)和\([i+1,k]\) ;

    ​ 由于$v[j,i] \lt v[i+1,n] \ , \ v[j,i] \ge v[i+1,k] $ ;

    ​ 所以$v[k+1,n] \gt v[j,i] \ge v[i+1,k] $ ;

    ​ 所以\(H'[1,k] + H'[k+1,n]\)一定合法且比\(H\)优;

  • 至此在不修改的时候可以 \(O(n)\) 解决问题;

  • Part 2

  • 考虑如何合并\([1,i-1 ]\)和\([i,i]\)和\([i+1,n]\),先讨论合并两个区间的情况;

  • 定理三

    ​ 两个区间的并的划分方案中任意一个划分点在子区间中也一定是划分点;

    从左到右执行,一定不会使左区间的划分点变多,从右往左同理,根据引理三得证,可以推广到多个区间的情况;

  • 根据引理三,设两个区间为\([1,mid]\)和\([mid+1,n]\),可以先处理出\(H'[1,mid-1]\)和\(H'[mid+1,n]\),根据定理三,为了方便修改意义区间元素直接是等值块,最终被合并的一定是左区间的一段后缀+右区间的一段前缀,令其为区间\([L_0,R_0]\),\(v[L_0,R_0]\) 表示区间平均值;

  • 引理四

    ​ 当加入到右区间的$ R \(时,设\) L_0 $是此时合并到的左端点,则:

    1.合法条件为:$v[L_0,R] \gt v_{L0-1} $ ,且具有可二分性;

    2.\(L_0\)是使得\(v[L,R]\)最大的\(L\);

    证明:

    ​ 由于栈是单增的并且在合并过程中,加入点的权值的变化是单调不增的,所以12得证;

  • 定理四

    ​ 对于右边的一个等值块\(R\),\(L\)是其在引理四中的\(L_0\),若:

    1.\(R<R_0\) 则有\(v[L,R] \ge v_{R+1}\) ;

    2.\(R \ge R_0\) 则有\(v[L,R] \lt v_{R+1}\) ;

    证明:

    ​ 由于\(R_0\)一定满足2,只需要证明对于一个满足的\(R\),\(>R\)的位置一定满足2即可;

    归纳假设\(R\)满足,左端点设为\(L\),对于\(R+1\),左端点设为\(L'\),有引理四2和假设得到:\(v[L',R] \le v[L,R] \lt v_{R+1}\),所以$v[L',R+1] \lt v_{R+1} \lt v_{R+2} $ ,得证;

  • 引理四和定理四在中间有一个修改点 \(i\) 的时候依旧适用;

  • 所以可以找出[1,i-1]的单调和[i+1,n]的单调栈(从n做到1再往后退栈),二分套二分即可;

#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
using namespace std;
const int N=100010;
int n,m,ny[N],pl,pr,len[N],ansl[N],ansr[N],pre[N],ql,qr,ans[N];
struct query{
int x,y,id;
query(int _x=0,int _y=0,int _id=0):x(_x),y(_y),id(_id){};
bool operator <(const query&A)const{return x<A.x;}
}Q[N];
struct data{
ll x,y,z;
data(ll _x=0,ll _y=0,ll _z=0):x(_x),y(_y),z(_z){};
data operator +(const data&A)const{return data(x+A.x,y+A.y,z+A.z);}
data operator -(const data&A)const{return data(x-A.x,y-A.y,z-A.z);}
bool operator <=(const data&A)const{return y*A.x<=A.y*x;}
bool operator >=(const data&A)const{return y*A.x>=A.y*x;}
}A[N],L[N],R[N],pR[N],sL[N],sR[N],psR[N],X,Y,Z;
data getL(int l,int r){return sL[r]-sL[l-1];}
data getR(int l,int r){return sR[r]-sR[l-1];}
int cal(data now){
int x=now.x,y=now.y%mod,z=now.z%mod;
int v=(ll)y*ny[x]%mod;
return (z-(ll)v*y*2%mod+(ll)v*v%mod*x%mod+mod)%mod;
}
void lowerL(){
int l=1,r=pl+1;
while(l<r){
int mid=(l+r+1)>>1;
Z=Y+getL(mid,pl);
if(Z<=L[mid-1])r=mid-1;
else l=mid;
}
Z=Y+getL(ql=l,pl);
}
void lowerR(){
int l=1,r=pr+1;
while(l<r){
int mid=(l+r+1)>>1;
Y=X+getR(mid,pr);
lowerL();
if(Z>=R[mid-1])r=mid-1;
else l=mid;
}
Y=X+getR(qr=r,pr);
lowerL();
}
int main(){
// freopen("sequence.in","r",stdin);
// freopen("sequence.out","w",stdout);
scanf("%d%d",&n,&m);ny[1]=1;
for(int i=2;i<=n;++i){ny[i]=1ll*(mod-mod/i)*ny[mod%i]%mod;}
for(int i=1;i<=n;++i){
int x;scanf("%d",&x);
A[i]=data(1,x,(ll)x*x%mod);
}
for(int i=1;i<=m;++i){
int x,y;scanf("%d%d",&x,&y);
Q[i]=query(x,y,i);
}
sort(Q+1,Q+m+1);
L[0]=data(1,0,0);
R[0]=data(1,1e9+1,0);
for(int i=n;~i;--i){
data now=A[i];
while(now>=R[pr])now=now+R[pr--];
len[i]=++pr;
pR[i]=R[pr];R[pr]=now;
psR[i]=sR[pr];sR[pr]=sR[pr-1]+now;
pre[i]=ansr[pr];ansr[pr]=(ansr[pr-1]+cal(now))%mod;
}
ans[0]=ansr[pr];
for(int i=1,tl=0,tr=1;i<=m;++i){
while(tr<Q[i].x+1){
R[pr]=pR[tr];
sR[pr]=psR[tr];
ansr[pr]=pre[tr];
pr=len[++tr];
}
while(tl<Q[i].x-1){
data now=A[++tl];
while(now<=L[pl])now=L[pl--]+now;
L[++pl]=now;
sL[pl]=sL[pl-1]+now;
ansl[pl]=(ansl[pl-1]+cal(now))%mod;
}
X=data(1,Q[i].y,(ll)Q[i].y*Q[i].y%mod);
lowerR();
ans[ Q[i].id ] = ((ll)ansl[ql-1] + ansr[qr-1] + cal(Z)) %mod;
}
for(int i=0;i<=m;++i)printf("%d\n",ans[i]);
return 0;
}

【loj3059】【hnoi2019】序列的更多相关文章

  1. 【洛谷5294】[HNOI2019] 序列(主席树维护单调栈+二分)

    点此看题面 大致题意: 给你一个长度为\(n\)的序列\(A\),每次询问修改一个元素(只对当前询问有效),然后让你找到一个不下降序列\(B\),使得这两个序列相应位置之差的平方和最小,并输出这个最小 ...

  2. 【题解】Luogu P5294 [HNOI2019]序列

    原题传送门 题意:给你一个长度为\(n\)的序列\(A\),每次询问修改一个元素(只对当前询问有效),然后让你找到一个不下降序列\(B\),使得这两个序列相应位置之差的平方和最小,并输出这个最小平方和 ...

  3. 【Luogu5294】[HNOI2019]序列

    题目链接 题意 给定一个序列,要求将它改造成一个非降序列,修改一个数的代价为其改变量的平方. 最小化总代价. 另有\(Q\) 次询问,每次修改一个位置上的数.(询问之间独立,互不影响) Sol 神仙 ...

  4. [HNOI2019]序列(单调栈+二分)

    通过打表证明发现答案就是把序列划分成若干段,每段的b都是这一段a的平均数.50分做法比较显然,就是单调栈维护,每次将新元素当成一个区间插入末尾,若b值不满足单调不降,则将这个区间与单调栈前一个区间合并 ...

  5. 题解 [HNOI2019]序列

    题目传送门 题目大意 给出一个\(n\)个数的数列\(A_{1,2,...,n}\),求出一个单调不减的数列\(B_{1,2,...,n}\),使得\(\sum_{i=1}^{n}(A_i-B_i)^ ...

  6. luogu P5294 [HNOI2019]序列

    传送门 这个什么鬼证明直接看uoj的题解吧根本不会证明 首先方案一定是若干段等值的\(B\),然后对于一段,\(B\)的值应该是\(A\)的平均值.这个最优方案是可以线性构造的,也就是维护以区间平均值 ...

  7. CSP-S2019「Symphony」

    NOTICE:如觉得本文有什么错误或不妥之处,欢迎评论区以及私信交流,反对乱喷,如有一些让人不爽的评论或人身攻击,带来的后果本人一律不负责 准备工作 Day-inf~Day-3 000 every d ...

  8. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

  9. LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分

    题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...

随机推荐

  1. 创建maven父项目以及子项目

    创建maven父项目以及子项目(Eclipse创建Maven Project跟Maven Module)https://blog.csdn.net/Mrsanger/article/details/8 ...

  2. 将 C++/WinRT 中的线程切换体验带到 C# 中来(WPF 版本)

    原文:将 C++/WinRT 中的线程切换体验带到 C# 中来(WPF 版本) 如果你要在 WPF 程序中使用线程池完成一个特殊的任务,那么使用 .NET 的 API Task.Run 并传入一个 L ...

  3. Spring-Cloud之Hystrix熔断器-5

    一.在分布式系统中,服务与服务之间的依赖错综复杂,一种不可避免的情况就是某些服务会出现故障,导致依赖于它们的其他服务出现远程调度的线程阻塞 Hystrix是Netflix 公司开源的一个项目,它提供了 ...

  4. 3.matplotlib绘制条形图

    plt.bar() # coding=utf-8 from matplotlib import pyplot as plt from matplotlib import font_manager my ...

  5. 提取线条的lines_color、lines_facet、 lines_gauss算子

    Halcon中线条提取的算子主要有: lines_color(Image : Lines : Sigma, Low, High, ExtractWidth, CompleteJunctions : ) ...

  6. linux搭建stm32开发环境

    下载stm32固件库 创建目录 libs目录放stm32固件库,src放用户源码,inc放用户头文件 # mkdir libs src inc 复制文件 将STM32F10x_StdPeriph_Li ...

  7. Jmeter计数器的使用-转载

    说一下jmeter中,配置元件-计数器的使用. 如果需要引用的数据量较大,且要求不能重复或者需要自增,那么可以使用计数器来实现. 1.启动jmeter,添加线程组,右键添加配置元件——计数器,如下图: ...

  8. MySQL MGR--数据同步原理

    MGR复制架构 在MySQL 5.7.17版本正式推出组复制(MySQL Group Repliation MGR),用来解决异步复制和半同步复制可能产生数据不一致的问题,组复制依靠分布式一致性协议( ...

  9. SQL Text Literals 文本

    Text Literals 文本 Use the text literal notation to specify values whenever string appears in the synt ...

  10. Python 软件安装

    安装Python解释器 Python目前已支持所有主流操作系统,在Linux,Unix,Mac系统上自带Python环境,在Windows系统上需要安装一下,超简单 打开官网https://www.p ...