【题解】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) ...
随机推荐
- hive基础知识三
1. 基本查询 注意 SQL 语言大小写不敏感 SQL 可以写在一行或者多行 关键字不能被缩写,也不能分行 各子句一般要分行写 使用缩进提高语句的可读性 1.1 全表和特定列查询 全表查询 selec ...
- Pytorch卷积神经网络识别手写数字集
卷积神经网络目前被广泛地用在图片识别上, 已经有层出不穷的应用, 如果你对卷积神经网络充满好奇心,这里为你带来pytorch实现cnn一些入门的教程代码 #首先导入包 import torchfrom ...
- JavaScript 整数转大写中文
function toChinese(money){ var chNum=['零','壹','贰','叁','肆','伍','陆','柒','捌','玖']; var maxnum=999999999 ...
- 运行java程序的时候出现Exception in thread "main" java.lang.UnsupportedClassVersionError:
1 Exception in thread "main" java.lang.UnsupportedClassVersionError: com/test/inherited/In ...
- Map、Set、List是否有序
首先我们应该清楚这个概念:这里的有序和无序不是指集合中的排序,而是是否按照元素添加的顺序来存储对象. list是按照元素的添加顺序来存储对象的,因此是有序的.他的实现类ArrayList.Linked ...
- 小程序组件--> 组件传参
小程序组件,在components文件夹右击-->创建文件夹-->右击-->新建component即可 创建一个组件 如果多个地方需要使用到,可以在app.json中加入一下代码,相 ...
- oracle 如何判断字符串中是否包含中文?超级简单!
1.情景展示 如何快速的判断出指定字符串中是否包含中文呢? 2.解决方案 通过length()和lengthb()函数的比对结果进行判断. lengthb(string)计算string所占的字节 ...
- 使用electron开发桌面级小程序自动部署系统
那一天我二十一岁,在我一生的黄金时代,我有好多奢望.我想爱,想吃,还想在一瞬间变成天上半明半暗的云,后来我才知道,生活就是个缓慢受锤的过程,人一天天老下去,奢望也一天天消逝,最后变得像挨了锤的牛一样. ...
- 【面向对象】第三单元总结——JML
梳理JML语言的理论基础.应用工具链情况 JML语言理论基础 JML(Java Modeling Language)是一种行为规范接口语言,通过使用不会被编译的注释形式,和固定关键字的语法,指定Jav ...
- 算法题----任意进制转换(C++)
#include <bits/stdc++.h> using namespace std; int toInt(char c) { // char c = s; if(c >= '0 ...