Loj #3059. 「HNOI2019」序列

给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k\)。第一次修改之前及每次修改之后,都要求你找到一个同样长度为 \(n\) 的单调不降序列 \(B_1, \ldots , B_n\),使得 \(\sum_{i=1}^n (A_i −B_i)^2\) 最小,并输出该最小值。需要注意的是每次操作的影响都是独立的,也即每次操作只会对当前询问造成影响。为了避免精度问题,我们保证这个最小值是个分数,也即能表示为两个非负整数相除的形式:\(x/y\)。那么你将要输出 \((x\times y^{P-2})\bmod P\) 的值,表示模意义下 \(x/y\) 的值。其中 \(P=998244353\) 是一个大质数。

输入格式

第一行两个非负整数 \(n,m\),代表序列长度和操作数。

第二行有 \(n\) 个由空格隔开的正整数,代表序列 \(A_1, \ldots , A_n\)。

接下来 \(m\) 行每行两个正整数 \(i, k\),代表将 \(A_i\) 修改为 \(k\)。

输出格式

输出 \(m + 1\) 行每行一个整数,第 \(i\) 行输出第 \(i − 1\) 次修改后的答案。特别的,第 \(1\) 行应为初始局面的答案。

数据范围与提示

对于前 \(10\%\) 的数据,保证 \(n, m \le 10\),\(k, A_i ≤ 1000\),且存在一种最优方案,使得 \(B_i\) 皆为整数。

对于前 \(30\%\) 的数据,保证 \(n, m \le 100\)。

对于另外 \(20\%\) 的数据,保证 \(m = 0\)。

对于另外 \(20\%\) 的数据,保证 \(n, m \le 3 \times 10^4\)。

对于所有数据,保证 \(3 \le n \le 10^5, 0 \le m \le 10^5, 1 \le k, A_i \le 10^9\)。

\(\\\)

Orz

假设没有修改,那这就是个经典问题(然而我不会)。

如果\(A\)也是个单调不下降序列,那么答案就是\(0\)。否则,对于\(A_i>A_{i+1}\)的情况,我们将\(A_i\)和\(A_{i+1}\)合并起来。合并后的块之间也存在这种关系,不过是拿块的平均值比较。

\(B_i\)就是\(i\)所在块的平均值。

我们先将询问离线下来,按位置排序。假设当前处理位置\(i\)的询问,我们维护两个栈,一个是从\(1\)到\(i-1\),按上述规则合并后的栈;一个数从\(i+1\)到\(n\)按上述规则合并后的栈。对于后一个栈,我们可以先从\(n\)到\(1\)维护一遍,记录下每个位置\(i\)加入栈中对这个栈的修改。然后依次回退。

假设修改后\(i\)所在的块为\([L,R]\),那么\([L,R]\),\([1,L-1]\)与\([R+1,n]\)分别形成的栈之间是相互独立的。这也就是维护两个栈的原因。

考虑求\([L,R]\)。我们先二分出\(R\),然后再找对应的\(L\),判断\([L,R]\)的平均值是否\(\leq\)\(R+1\)所在块的平均值,如果是,将二分边界往左移;否则往右移(注意这里二分的是完整的块)。因为如果以\(R\)所在块为右端点成立,那么我们加入\(R\)所在块右侧的块依然成立,所以答案是有单调性的。找\(R\)对应的\(L\)用的也是相同的原理。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 100005 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} const ll mod=998244353;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
} int n,m;
ll a[N]; struct node {
ll sum,size;
int l,r;
long double key;
node() {}
node(ll _sum,ll _size,int _l,int _r) {
sum=_sum,size=_size;
l=_l,r=_r;
key=(long double)sum/size;
}
}; node operator +(const node &a,const node &b) {return node(a.sum+b.sum,a.size+b.size,min(a.l,b.l),max(a.r,b.r));} int cal() {
static node st[N];
int top;
st[top=1]=node(a[1],1,1,1);
node tem;
for(int i=2;i<=n;i++) {
tem=node(a[i],1,i,i);
while(top>=1&&st[top].key>=tem.key) {
tem=tem+st[top];
top--;
}
st[++top]=tem;
}
int now=1;
ll ans=0;
for(int i=1;i<=top;i++) {
ll ave=st[i].sum%mod*ksm(st[i].size,mod-2)%mod;
for(int j=1;j<=st[i].size;j++,now++) {
(ans+=(a[now]-ave)*(a[now]-ave))%=mod;
}
}
return ans;
} int rx[N],lx[N];
ll sum[N];
ll suf[N],pre[N],sums[N]; int t1=0,t2=0;
node pres[N],sufs[N];
int pos[N];
node del[N]; struct query {
int id,k;
}; vector<query>q[N];
int Find_left(int x,ll val,int R) {
if(x==1) return 1;
if(pres[t1].key<=(long double)(sum[R]-sum[x]+val)/(R-x+1)) return x;
int l=1,r=t1,mid;
while(l<r) {
mid=l+r+1>>1;
int L=pres[mid].l;
long double k=(long double)(sum[R]-sum[L-1]-a[x]+val)/(R-L+1);
if(pres[mid-1].key<=k) l=mid;
else r=mid-1;
}
return pres[l].l;
} int Find_right(int x,ll val) {
if(x==n) return n;
int L=Find_left(x,val,x);
if(sufs[t2].key>=(long double)(sum[x-1]-sum[L-1]+val)/(x-L+1)) return x;
int l=1,r=t2,mid;
while(l<r) {
mid=l+r+1>>1;
int R=sufs[mid].r;
int L=Find_left(x,val,R);
long double k=(long double)(sum[R]-sum[L-1]-a[x]+val)/(R-L+1);
if(k>sufs[mid-1].key) r=mid-1;
else l=mid;
}
return sufs[l].r;
} ll Ans[N]; int main() {
n=Get(),m=Get();
for(int i=1;i<=n;i++) a[i]=Get(); for(int i=1;i<=n;i++) {
sum[i]=sum[i-1]+a[i];
sums[i]=(sums[i-1]+a[i]*a[i])%mod;
}
cout<<cal()<<"\n"; for(int i=1;i<=m;i++) {
int x=Get(),k=Get();
q[x].push_back((query) {i,k});
} node tem;
for(int i=n;i>=1;i--) {
tem=node(a[i],1,i,i);
pos[i]=t2;
while(t2&&sufs[t2].key<=tem.key) {
tem=tem+sufs[t2];
t2--;
}
del[i]=sufs[++t2];
sufs[t2]=tem; ll ave=tem.sum%mod*ksm(tem.size,mod-2)%mod;
int R=tem.r;
suf[i]=((suf[R+1]+sums[R]-sums[i-1]+ave*ave%mod*(R-i+1)-2*ave*((sum[R]-sum[i-1])%mod))%mod+mod)%mod;
} pres[0].key=0;
sufs[0].key=1e9+7;
for(int i=1;i<=n;i++) {
sufs[t2]=del[i];
t2=pos[i];
for(int j=0;j<q[i].size();j++) {
int x=i;
ll k=q[i][j].k;
ll dlt=(k-a[x]+mod)%mod,dlts=(k*k-a[x]*a[x])%mod+mod;
int R=Find_right(x,k),L=Find_left(i,k,R);
ll ans=(pre[L-1]+suf[R+1])%mod;
ll ave=(sum[R]-sum[L-1]+dlt+mod)%mod*ksm(R-L+1,mod-2)%mod;
ans=((ans +sums[R]-sums[L-1]+dlts +ave*ave%mod*(R-L+1)%mod -2*ave*((sum[R]-sum[L-1]+dlt)%mod))%mod+mod)%mod;
Ans[q[i][j].id]=ans;
}
tem=node(a[i],1,i,i);
while(t1&&pres[t1].key>=tem.key) {
tem=tem+pres[t1];
t1--;
}
pres[++t1]=tem; ll ave=tem.sum%mod*ksm(tem.size,mod-2)%mod;
int L=tem.l;
pre[i]=((pre[L-1]+sums[i]-sums[L-1]+ave*ave%mod*(i-L+1)%mod-2*ave*((sum[i]-sum[L-1])%mod))%mod+mod)%mod;
}
for(int i=1;i<=m;i++) cout<<Ans[i]<<"\n";
return 0;
}

Loj #3059. 「HNOI2019」序列的更多相关文章

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

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

  2. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

  3. Loj 3058. 「HNOI2019」白兔之舞

    Loj 3058. 「HNOI2019」白兔之舞 题目描述 有一张顶点数为 \((L+1)\times n\) 的有向图.这张图的每个顶点由一个二元组 \((u,v)\) 表示 \((0\le u\l ...

  4. Loj #3055. 「HNOI2019」JOJO

    Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...

  5. Loj #3057. 「HNOI2019」校园旅行

    Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...

  6. loj #2051. 「HNOI2016」序列

    #2051. 「HNOI2016」序列 题目描述 给定长度为 n nn 的序列:a1,a2,⋯,an a_1, a_2, \cdots , a_na​1​​,a​2​​,⋯,a​n​​,记为 a[1: ...

  7. LOJ 3158: 「NOI2019」序列

    题目传送门:LOJ #3158. 题意简述: 给定两个长度为 \(n\) 的正整数序列 \(a,b\),要求在每个序列中都选中 \(K\) 个下标,并且要保证同时在两个序列中都被选中的下标至少有 \( ...

  8. LOJ #2183「SDOI2015」序列统计

    有好多好玩的知识点 LOJ 题意:在集合中选$ n$个元素(可重复选)使得乘积模$ m$为$ x$,求方案数对$ 1004535809$取模 $ n<=10^9,m<=8000且是质数,集 ...

  9. LOJ 3057 「HNOI2019」校园旅行——BFS+图等价转化

    题目:https://loj.ac/problem/3057 想令 b[ i ][ j ] 表示两点是否可行,从可行的点对扩展.但不知道顺序,所以写了卡时间做数次 m2 迭代的算法,就是每次遍历所有不 ...

随机推荐

  1. 手把手用Monkey写一个压测脚本

    版权声明: 本账号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影所有. 允许有条件转载,转载请附带底部二维码. 一.为什么需要一个测试脚本? 昨天讲解了Android Monkey命 ...

  2. 知其所以然~分布式事务cap

    背景 一致性是一个抽象的.具有多重含义的计算机术语,在不同应用场景下,有不同的定义和含义.在传统的IT时代,一致性通常指强一致性,强一致性通常体现在你中有我.我中有你.浑然一体:而在互联网时代,一致性 ...

  3. SpringBoot+gradle项目构建war

    前言 一开始觉得这并不是什么很难的事情,但是踩坑了之后才发现... 步骤 1.在build.gradle配置文件中加入apply plugin: 'war' 很多人以为这样就完事了,我一开始也是这样认 ...

  4. Python:游戏:测试打字速度

    现在写书的人真是一点责任心都没有,最近看了几本书,其中的代码都存在错误. 最近迷恋 Python 游戏,买了<Python游戏编程入门>[美] Jonathan S·Harbour 著 一 ...

  5. RabbitMQ消息队列(十一)-如何实现高可用

    在前面讲到了RabbitMQ高可用集群的搭建,但是我们知道只是集群的高可用并不能保证应用在使用消息队列时完全没有问题,例如如果应用连接的RabbitMQ集群突然宕机了,虽然这个集群时可以使用的,但是应 ...

  6. FALSE

    Topic Link http://ctf5.shiyanbar.com/web/false.php 1)源代码分析 2)发现 输入的name和password不能相等相等但是sha1值却相等这在sh ...

  7. 全图化引擎(AI·OS)中的编译技术

    全图化引擎又称算子执行引擎,它的介绍可以参考从HA3到AI OS -- 全图化引擎破茧之路.本文从算子化的视角介绍了编译技术在全图化引擎中的运用.主要内容有: 1. 通过脚本语言扩展通用算子上的用户订 ...

  8. JaveWeb学习之Servlet(一):Servlet生命周期和加载机制

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-07-22/19.html 作者:夜月归途 出处:http://www.guitu ...

  9. C# 绘制Word形状——基本形状、组合形状

    一.序言 在Office Word中,支持在Word文档中插入类型非常丰富的形状,包括线条.矩形.基本形状(诸如圆形.多边形.星形.括号.笑脸等等图形).箭头形状.公式形状.流程图.旗帜图形.标注图形 ...

  10. 从URL到看到网页的过程

    从我们输入URL并按下回车键到看到网页结果之间发生了什么?换句话说,一张网页,要经历怎样的过程,才能抵达用户面前?下面来从一些细节上面尝试一下探寻里面的秘密. 前言:键盘与硬件中断 说到输入URL,当 ...