【题解】Luogu P5294 [HNOI2019]序列
原题传送门
题意:给你一个长度为\(n\)的序列\(A\),每次询问修改一个元素(只对当前询问有效),然后让你找到一个不下降序列\(B\),使得这两个序列相应位置之差的平方和最小,并输出这个最小平方和
观察样例说明,发现一个很有趣的性质,\(B\)中数字相同的一段的数字正好是\(A\)中这段数字的平均数
那我们就珂以猜想:最优解的形式一定为分成若干段,每一段的\(B_i\)即取其中\(A_i\)的平均数,同时保证\(B\)的有序性(这篇论文好像有证明)
如何求出最优的\(B\)?我们珂以使用单调栈
考虑枚举\(i\),将\([i,i]\)塞进单调栈中,然后比较栈顶区间的平均值和栈顶下面一个区间的平均值。如果栈顶区间平均值较小,则不满足不下降性质,因此我们将这两个区间合并,然后继续与再下面一个区间进行比较……
如何合并答案?我们只需要维护维护区间元素个数,区间元素和,区间元素平方和即可(像方差那题一样)
接下来考虑如何修改。我们在求整个序列的最优解时,同主席树维护\(A\)每个前缀的单调栈\(pre\)。类似的,从后向前也跑一次,用主席树维护\(A\)每个后缀的单调栈\(suf\)。
假设我们将位置\(pos\)的值修改成了\(val\)。设修改后最优解中\(pos\)所在的区间为\([L,R]\),如果我们能够快速求出\(L,R\)我们就珂以快速得出答案。
注意到单调栈中的一段数的任何前缀平均值都大于去掉该前缀剩余的数的平均值,所以\(L\)为\(pre[pos-1]\)中某个区间的\(L\)或\(pos\),\(R\)为\(suf[pos+1]\)中某个区间的\(R\)或\(pos\)
二分\(R\)的位置,在主席树\(pre[pos-1]\)上二分求出符合的\(L\),判断是否合法。最终找到最小的合法的\(R\),算出\(L\),就珂以求出答案了
#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define mod 998244353
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline int Max(register int a,register int b)
{
return a>b?a:b;
}
int n,m,pre[N],prelen[N],suf[N],suflen[N],tot;
pair<int,int> s[N];
ll inv[N];
struct node{
ll x,y,z;
node(){x=y=z=0;}
node(register ll _x,register ll _y,register ll _z){x=_x,y=_y,z=_z;}
inline ll cal()
{
return (z+mod-(y%mod)*(y%mod)%mod*inv[x]%mod)%mod;
}
}sum[N];
inline node Init(register ll v)
{
return node(1,v,v*v%mod);
}
inline node operator + (node a,node b){return (node){a.x+b.x,a.y+b.y,(a.z+b.z)%mod};}
inline node operator - (node a,node b){return (node){a.x-b.x,a.y-b.y,(a.z-b.z+mod)%mod};}
inline bool operator < (node a,node b){
if(!a.x)
return b.x;
if(!b.x)
return 0;
return (double)a.y/a.x<(double)b.y/b.x;
}
inline node cal(register int l,register int r)
{
if(!l&&!r)
return node(0,0,0);
return sum[r]-sum[l-1];
}
struct seg{
int ls,rs,L,R,ans,Rp;
inline void merge(register seg a,register seg b)
{
L=a.L;
R=Max(a.R,b.R);
ans=(a.ans+b.ans)%mod;
Rp=a.Rp;
}
}tr[N*80];
inline void modify(register int &x,register int l,register int r,register int pos,register int L,register int R)
{
tr[++tot]=tr[x];
x=tot;
if(l==r)
tr[x]=(seg){0,0,L,R,cal(L,R).cal(),R};
else
{
int mid=l+r>>1;
if(pos<=mid)
modify(tr[x].ls,l,mid,pos,L,R);
else
modify(tr[x].rs,mid+1,r,pos,L,R);
tr[x].merge(tr[tr[x].ls],tr[tr[x].rs]);
}
}
inline int query_suf(register int x,register int l,register int r,register int pos)
{
if(l==r)
return tr[x].R;
int mid=l+r>>1;
if(pos<=mid)
return query_suf(tr[x].ls,l,mid,pos);
else
return query_suf(tr[x].rs,mid+1,r,pos);
}
inline int query_pre(register int x,register int l,register int r,register int pos,register node &val)
{
if(r<=pos)
{
node twh=cal(tr[x].L,tr[x].R),tlf=cal(tr[x].L,tr[x].Rp);
if(!(tlf<val+twh))
{
val=val+twh;
return 0;
}
if(l==r)
return tr[x].R;
}
int mid=l+r>>1,res=0;
if(pos>mid)
res=query_pre(tr[x].rs,mid+1,r,pos,val);
if(res)
return res;
return query_pre(tr[x].ls,l,mid,pos,val);
}
int main()
{
n=read(),m=read();
inv[1]=1;
for(register int i=2;i<=n;++i)
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
for(register int i=1;i<=n;++i)
sum[i]=sum[i-1]+Init(read());
for(register int i=1,top=0;i<=n;++i)
{
pre[i]=pre[i-1];
int l=i;
while(top&&!(cal(s[top].first,s[top].second)<cal(l,i)))
modify(pre[i],1,n,top,0,0),l=s[top--].first;
s[prelen[i]=++top]=make_pair(l,i);
modify(pre[i],1,n,top,l,i);
}
for(register int i=n,top=0;i;--i)
{
suf[i]=suf[i+1];
int r=i;
while(top&&!(cal(i,r)<cal(s[top].first,s[top].second)))
modify(suf[i],1,n,top,0,0),r=s[top--].second;
s[suflen[i]=++top]=make_pair(i,r);
modify(suf[i],1,n,top,i,r);
}
write(tr[pre[n]].ans),puts("");
while(m--)
{
int x=read(),y=read();
int l=0,r=suflen[x+1]-1,res=r+1;
while(l<=r)
{
int mid=l+r>>1;
int rp=mid?query_suf(suf[x+1],1,n,suflen[x+1]-mid+1):x;
node val=Init(y)+cal(x+1,rp);
int lp=x>1?query_pre(pre[x-1],1,n,prelen[x-1],val):1;
if(val<cal(rp+1,query_suf(suf[x+1],1,n,suflen[x+1]-mid)))
res=mid,r=mid-1;
else
l=mid+1;
}
int rp=res?query_suf(suf[x+1],1,n,suflen[x+1]-res+1):x;
node val=Init(y)+cal(x+1,rp);
int lp=x>1?query_pre(pre[x-1],1,n,prelen[x-1],val):1;
write((val.cal()+tr[pre[lp]].ans+tr[suf[rp+1]].ans)%mod),puts("");
}
return 0;
}
【题解】Luogu P5294 [HNOI2019]序列的更多相关文章
- luogu P5294 [HNOI2019]序列
传送门 这个什么鬼证明直接看uoj的题解吧根本不会证明 首先方案一定是若干段等值的\(B\),然后对于一段,\(B\)的值应该是\(A\)的平均值.这个最优方案是可以线性构造的,也就是维护以区间平均值 ...
- [luogu P3648] [APIO2014]序列分割
[luogu P3648] [APIO2014]序列分割 题目描述 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列.为了得到k+1个子序 ...
- [题解] Luogu P5446 [THUPC2018]绿绿和串串
[题解] Luogu P5446 [THUPC2018]绿绿和串串 ·题目大意 定义一个翻转操作\(f(S_n)\),表示对于一个字符串\(S_n\), 有\(f(S)= \{S_1,S_2,..., ...
- 【题解】Luogu P2572 [SCOI2010]序列操作
原题传送门:P2572 [SCOI2010]序列操作 这题好弱智啊 裸的珂朵莉树 前置芝士:珂朵莉树 窝博客里对珂朵莉树的介绍 没什么好说的自己看看吧 操作1:把区间内所有数推平成0,珂朵莉树基本操作 ...
- 【题解】Luogu P5470 [NOI2019]序列
原题传送门 同步赛上我一开始想了个看似正确却漏洞百出的贪心:按\(a_i+b_i\)的和从大向小贪心 随便想想发现是假的,然后就写了个28pts的暴力dp 杜神后半程说这题就是个贪心,但我没时间写了 ...
- 题解 [HNOI2019]序列
题目传送门 题目大意 给出一个\(n\)个数的数列\(A_{1,2,...,n}\),求出一个单调不减的数列\(B_{1,2,...,n}\),使得\(\sum_{i=1}^{n}(A_i-B_i)^ ...
- 【题解】Luogu P5288 [HNOI2019]多边形
原题传送门 HN的题目就是毒瘤 我们有以下猜想: 1.最后所有的线都连到了n号点上 2.最小步数应该为n-3-已经连到n号点的线段数量 本来有些边\((a_i,n)\)会将整个图分割成很多个区间.对于 ...
- 洛谷 题解 UVA1626 【括号序列 Brackets sequence】
看还没有人发记搜的题解,赶紧来水发一篇 我们定义dp[i][j]为区间i~j内最少添加几个括号才能把这个串变成正规括号序列. 考虑四种情况 i>j不存在这种子串,返回0 i==j子串长度为1无论 ...
- 题解 最长上升序列2 — LIS2
最长上升序列2 - LIS2 Description 已知一个 1 ∼ N 的排列的最长上升子序列长度为 K ,求合法的排列个数. Input 输入一行二个整数 N , K ( K ≤ N ≤ 15) ...
随机推荐
- 【JZOJ6213】【20190613】String
题目 \(n \le 10^{18} \ , \ |T| \le 10^5\) 题解 显然,最少的操作次数一定是贪心地能匹配就匹配 我们可以建出\(T\)的SAM,把SAM不能走的边补到根的后继节点 ...
- 将图片文件转成BASE64格式
html5Reader (file, item) { const reader = new FileReader() reader.onload = (e) => { this.$set(ite ...
- Fluent Meshing生成空心球体网格(空心部分不生成网格)
原视频下载地址: https://pan.baidu.com/s/1OJdHuIJd7pAr7cHgVCD_0g 密码: puiq
- E4A碰到打开自动闪退又自动打开又闪退一直循环的问题
E4A碰到打开自动闪退又自动打开又闪退一直循环的问题 这几天写了一个脚本,自己手机上,模拟器上,均测试没有问题,可以正常操作 发给另一个人,他那边居然没有一个设备能运行成功! 而且一直闪退,闪退后又打 ...
- svn无法还原 、svn无法更新
报错: Previous operation has not finished; run 'cleanup' if it was interrupted 上一个操作尚未完成:如果中断,请运行“清理”
- java并发编程(四) 线程池 & 任务执行、终止源码分析
参考文档 线程池任务执行全过程:https://blog.csdn.net/wojiaolinaaa/article/details/51345789 线程池中断:https://www.cnblog ...
- Xmanager PowerSuite 6企业版详细安装破解教程,解决评估过期问题(附注册机,全网独家可用),非学校/家庭免费版
title: "Xmanager PowerSuite 6企业版详细安装破解教程,解决评估过期问题(附注册机,全网独家可用),非学校/家庭免费版" categories: soft ...
- Windows下将网络共享目录挂载到指定文件夹
简述 因为某些原因,设计好的目录结构是不能动的,因此需要将网络共享目录挂载到指定目录下,以便扩容. 在Linux下这完全没有问题,但是Windows下的操作就稍微复杂一点. 1.直接使用net use ...
- session与getSession()用法总结
一.session 1.session的过期时间是从什么时候开始计算的?是从一登录就开始计算还是说从停止活动开始计算? 从session不活动的时候开始计算,如果session一直活动,session ...
- sublime px转rem的方法【亲测有效】
在开发手机网站的时候,我们经常会用到rem来作为单位,但是手动转rem太麻烦了,那么怎么办呢?sublime安装cssrem来进行快捷操作. 安装过程: 第一步:去git上克隆 https://git ...