点此看题面

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

如何预处理

首先,仔细观察样例解释,我们可以发现一个有趣的性质:对于\(B\)序列中相同的一段元素,它们在\(A\)序列中恰好是这一段区间中所有数的平均数。

因此,我们大胆猜测:我们可以把\(A\)序列划分成若干段,然后求出每段的平均值,就可以求出最后的\(B\)序列。

那么,现在我们要做的,就是如何划分的问题。

一个显然的结论,肯定是划分出越多的区间越优。

但我们如何判断是否可以划分一个区间呢?

这就可以通过单调栈维护,来预处理出答案了。

我们考虑枚举\(i\),然后对于每个\(i\),先将区间\([i,i]\)扔入单调栈中。

然后,我们每次比较栈顶区间的平均值栈顶下面一个区间的平均值,如果栈顶区间平均值较小,则不满足不下降性质,因此我们将这两个区间合并,然后继续与再下面一个区间比较。(注:其实等于也可以合并,不会影响平均值)

于是新问题来了,如何维护并合并区间信息?

设平均值\(d=\frac{a_1+a_2+...+a_x}x\),则一个区间内的答案是:

\[(a_1-d)^2+(a_2-d)^2+...+(a_x-d)^2
\]

然后公式展开得到:

\[a_1^2-2a_1d+d^2+a_2^2-2a_2d+d^2+...+a_x^2-2a_xd+d^2
\]

用加法交换律和加法结合律改变一下顺序得到:

\[(a_1^2+a_2^2+...+a_x^2)-d(2a_1+2a_2+...+2a_x-xd)
\]

将\(d\)的值代入得到:

\[(a_1^2+a_2^2+...+a_x^2)-\frac{a_1+a_2+...+a_x}x\cdot(2a_1+2a_2+...+2a_x-a_1-a_2-...-a_x)
\]

\[(a_1^2+a_2^2+...+a_x^2)-\frac{(a_1+a_2+...+a_x)^2}x
\]

所以,我们只需要维护区间元素个数区间元素和区间元素平方和三个值就能完成求解答案和合并啦。

具体实现详见代码。

如何完成询问

设修改的位置为\(x,y\),则可以发现,对于前\(x-1\)个位置,它的单调栈操作过程必然与初始化时一样。

而加入第\(x\sim n\)个数之后,对于第\(x-1\)个单调栈,它大致可以表示成这个样子:

那么,我们只要找到\(L,R\),并确定第\(1\sim L-1,R+1\sim n\)个元素的答案,就可以确定最终答案了。

先考虑我们要用到第\(x-1\)个单调栈,因此我们就可以用一个主席树来维护单调栈,并记录下每个位置此时的答案以及单调栈的大小,然后就能很方便的求出第\(1\sim L-1\)的答案了。

再考虑第\(R+1\sim n\)个元素的答案就很简单了,直接采取与上面预处理类似的方式,倒着做一遍单调栈,预处理出后缀的全部信息即可。

于是,现在仅剩的问题就是,如何找到\(L,R\)。

可以发现,这显然具有可二分性

因此我们先二分一个右端点,然后对于这个右端点,我们再在主席树上二分求出其对应的左端点,并以此来验证这一段的平均值是否小于等于右端点的后一段的平均值即可。

注意对于栈中的元素我们肯定是整段整段取的,因为我们其实模拟了一个将元素加入栈的过程,而这一过程只会将某些段的元素合并,因此只能取整段。

具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define RL Reg LL
#define Con const
#define CI Con int&
#define CL Con LL&
#define I inline
#define W while
#define N 100000
#define Log 20
#define LL long long
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Dec(x,y) ((x-=(y))<0&&(x+=X))
using namespace std;
int n,a[N+5],Inv[N+5],rt1[N+5],rt2[N+5],top1[N+5],top2[N+5],ans1[N+5],ans2[N+5];
I int XSum(CI x,CI y) {return x+y>=X?x+y-X:x+y;}
I int XSub(CI x,CI y) {return x<y?x-y+X:x-y;}
struct data
{
int t,ps;LL s;I data(CI x=0,CL y=0,CI z=0):t(x),s(y),ps(z){}
I data operator + (Con data& o) {return data(t+o.t,s+o.s,XSum(ps,o.ps));}
I data operator - (Con data& o) {return data(t-o.t,s-o.s,XSub(ps,o.ps));}
I bool operator < (Con data& o) Con {return o.t?(t?1.0*s/t<1.0*o.s/o.t:1):0;}
I bool operator <= (Con data& o) Con {return t?(o.t?1.0*s/t<=1.0*o.s/o.t:0):1;}
I int GV() {return XSub(ps,1LL*(s%X)*(s%X)%X*Inv[t]%X);}
}s[N+5],S[N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
I void clear() {fwrite(FO,1,C,stdout),C=0;}
}F;
class ChairmanTree//主席树维护单调栈
{
private:
#define L l,mid,O[rt].S[0]
#define R mid+1,r,O[rt].S[1]
#define PushUp(x)\
(\
O[x].l=O[O[x].S[0]].l,O[x].r=O[O[x].S[O[x].S[1]>0]].r,\
O[x].k=O[O[x].S[0]].k\
)//上传信息
int tot;struct node {int l,r,k,S[2];}O[N*Log<<1];
public:
I void Update(CI l,CI r,int& rt,CI p,CI vl,CI vr)//修改单调栈中的值
{
if(O[++tot]=O[rt],rt=tot,l==r) return (void)(O[rt].l=vl,O[rt].r=O[rt].k=vr);
RI mid=l+r>>1;p<=mid?Update(L,p,vl,vr):Update(R,p,vl,vr),PushUp(rt);
}
I int Query1(CI l,CI r,CI rt,CI p,data& v)//主席树上二分左端点,用v存储下取到的段的信息
{
if(r<=p)
{
data dl=s[O[rt].k]-s[O[rt].l-1],dr=s[O[rt].r]-s[O[rt].k];
if(dr+v<=dl) return v=v+s[O[rt].r]-s[O[rt].l-1],0;
if(l==r) return O[rt].r;
}
RI mid=l+r>>1,res;return p>mid&&(res=Query1(R,p,v))?res:Query1(L,p,v);
}
I int Query2(CI l,CI r,CI rt,CI p)//求出第p个位置右端点的值
{
if(l==r) return O[rt].r;RI mid=l+r>>1;
return p<=mid?Query2(L,p):Query2(R,p);
}
}C;
int main()
{
RI Qtot,i,x,y,t,T,l,r,mid,p1,p2,ans=0;data v;
for(F.read(n,Qtot),i=1;i<=n;++i) F.read(a[i]),s[i]=s[i-1]+data(1,a[i],1LL*a[i]*a[i]%X);//预处理信息前缀和
for(Inv[1]=1,i=2;i<=n;++i) Inv[i]=1LL*Inv[X%i]*(X-X/i)%X;//预处理逆元
for(T=0,i=1;i<=n;++i)//正着做一遍单调栈
{
S[++T]=data(1,a[i],1LL*a[i]*a[i]%X),rt1[i]=rt1[i-1];//加入栈
W(T^1&&S[T]<=S[T-1]) S[T-1]=S[T-1]+S[T],--T;//对于栈顶不合法元素进行合并
C.Update(1,n,rt1[i],top1[i]=T,i-S[T].t+1,i),ans1[i]=XSum(ans1[i-S[T].t],S[T].GV());//主席树上修改,记录下每个位置此时的答案以及单调栈的大小
}
for(T=0,i=n;i;--i)//倒着做一遍单调栈,与上面类似
{
S[++T]=data(1,a[i],1LL*a[i]*a[i]%X),rt2[i]=rt2[i+1];
W(T^1&&S[T-1]<=S[T]) S[T-1]=S[T-1]+S[T],--T;//注意比较顺序相反
C.Update(1,n,rt2[i],top2[i]=T,i,i+S[T].t-1),ans2[i]=XSum(ans2[i+S[T].t],S[T].GV());
}
F.writeln(ans1[n]);W(Qtot--)//处理询问
{
F.read(x,y),t=a[x],a[x]=y,l=0,r=top2[x+1]-1;//读入,确定二分上下界
#define Work(pos)\
p2=(pos)?C.Query2(1,n,rt2[x+1],top2[x+1]-(pos)+1):x,\
v=data(1,a[x],1LL*a[x]*a[x]%X)+s[p2]-s[x],\
p1=x^1?C.Query1(1,n,rt1[x-1],top1[x-1],v):x
W(l<=r) mid=l+r>>1,Work(mid),//二分
v<s[C.Query2(1,n,rt2[x+1],top2[x+1]-mid)]-s[p2]?r=mid-1:l=mid+1;//验证
Work(r+1),F.writeln(XSum(v.GV(),XSum(ans1[p1],ans2[p2+1]))),a[x]=t;//求解并输出答案
//这里p1不用减1是因为它本来就是取左端点前一段的r值,而p2刚好是取这一段的r值因此要加1
}return F.clear(),0;
}

【洛谷5294】[HNOI2019] 序列(主席树维护单调栈+二分)的更多相关文章

  1. 洛谷P1823 [COI2007] Patrik 音乐会的等待(单调栈+二分查找)

    洛谷P1823 [COI2007] Patrik 音乐会的等待(单调栈+二分查找) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1333275 这个题不是很 ...

  2. [CSP-S模拟测试]:陶陶摘苹果(线段树维护单调栈)

    题目传送门(内部题116) 输入格式 第一行两个整数$n,m$,如题 第二行有$n$个整数表示$h_1-h_n(1\leqslant h_i\leqslant 10^9)$ 接下来有$m$行,每行两个 ...

  3. 【ZJOI2017 Round2练习&BZOJ4826】D1T2 sf(主席树,单调栈)

    题意: 思路:From http://blog.csdn.net/neither_nor/article/details/70211150 对每个点i,单调栈求出左边和右边第一个大于i的位置,记为l[ ...

  4. 洛谷P2617 Dynamic Rankings (主席树)

    洛谷P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a ...

  5. 洛谷P3567 KUR-Couriers [POI2014] 主席树/莫队

    正解:主席树/莫队 解题报告: 传送门! 这题好像就是个主席树板子题的样子,,,? 毕竟,主席树的最基本的功能就是,维护一段区间内某个数字的个数 但是毕竟是刚get到主席树,然后之前做的一直是第k大, ...

  6. 洛谷P3567[POI2014]KUR-Couriers(主席树+二分)

    题意:给一个数列,每次询问一个区间内有没有一个数出现次数超过一半 题解: 最近比赛太多,都没时间切水题了,刚好日推了道主席树裸题,就写了一下 然后 WA80 WA80 WA0 WA90 WA80 ?? ...

  7. 洛谷P4602 [CTSC2018]混合果汁(主席树)

    题目描述 小 R 热衷于做黑暗料理,尤其是混合果汁. 商店里有 nn 种果汁,编号为 0,1,\cdots,n-10,1,⋯,n−1 . ii 号果汁的美味度是 d_idi​ ,每升价格为 p_ipi ...

  8. 洛谷P3567 [POI2014]KUR-Couriers 主席树

    挺裸的,没啥可讲的. 不带修改的主席树裸题 Code: #include<cstdio> #include<algorithm> using namespace std; co ...

  9. 洛谷 P4198 楼房重建 线段树维护单调栈

    P4198 楼房重建 题目链接 https://www.luogu.org/problemnew/show/P4198 题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上 ...

随机推荐

  1. PIE SDK算法的异步调用

    1.算法功能简介 异步方法一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作.异步方法通常会在另外一个线程中,“真实”地执行着.整个过程,不会阻碍调用者的工作. PIE SDK支持算法功能的执 ...

  2. 安装软件或运行软件时提示缺少api-ms-win-crt-runtime库解决方法

    最近碰到一个问题,在我软件安装或运行时会提示缺少api-ms-win-crt-runtime-|1-1-0.dll 当然第一个想到的是运行库没有装,但是很清楚的是我的电脑是装过vc_redist_20 ...

  3. 《The Python Standard Library》——http模块阅读笔记2

    http.server是用来构建HTTP服务器(web服务器)的模块,定义了许多相关的类. 创建及运行服务器的代码一般为: def run(server_class=HTTPServer, handl ...

  4. git安装以及webstorm配置git

    下载及安装请移步   https://www.cnblogs.com/specter45/p/github.html 用webstorm上传代码时,首先要先下载git,网址一搜就可以搜到,然后开始配置 ...

  5. 求入栈顺序为1234……N的序列的所有可能的出栈序列

    class Program { private static void Fun(int x, int n, Stack<int> stack, List<int> outLis ...

  6. Long 和 Integer

    Integer 32位 其范围为 -2^31 到 2^31-1 之间,所以最大值是 2^31-1 Long 64位

  7. Linux下MySQL数据库主从同步配置

    说明: 操作系统:CentOS 5.x 64位 MySQL数据库版本:mysql-5.5.35 MySQL主服务器:192.168.21.128 MySQL从服务器:192.168.21.129 准备 ...

  8. pat1011. World Cup Betting (20)

    1011. World Cup Betting (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Wit ...

  9. [转]ASP.NET MVC 5 List Editor with Bootstrap Modals

    本文转自:https://www.codeproject.com/articles/786085/asp-net-mvc-list-editor-with-bootstrap-modals With ...

  10. UML建模—EA的使用起步

    Enterprise Architect(EA) 是一个功能比较强悍的建模工具. 对于一个软件设计者来说,从需求分析到业务设计.类模型设计.数据库设计到测试.发布.部署等一系列软件设计必须的操作都可以 ...