题意
给定一个序列,要求将它改造成一个非降序列,修改一个数的代价为其改变量的平方。
最小化总代价。
另有 Q Q Q 次询问,每次修改一个位置上的数。(询问之间独立,互不影响)
Sol
神仙 保序回归 问题,完全不会。
首先是一个暴力的每次 O ( n ) O(n) O(n) 做法。
结论是: 最后的结果序列一定是一段段的相同的数,其值为段中所有元素的平均数。
所以暴力就是维护一个单调栈。
每次加入一个数后形成一段。
然后不断比较栈顶的段和下面一个段的平均数的大小,如果栈顶小一些就把它和下面那个段合并。
然后考虑多组询问。
显然每次重新计算所有的数太呆了,有很多重复计算且没有必要的地方。
一个很直观的想法就是考虑求出最后修改的数所在段的左右端点,这样我们维护一个前缀后缀的答案后就能够 O ( 1 ) O(1) O(1) 算出最后的答案了。
所以我们先对前后缀分别维护好答案以及单调栈(用可持久化线段树)。
发现如果我们确定了左端点那么右端点是唯一确定的。
首先我们要知道从后往前做上面的贪心也是正确的。
然后你现在从后往前已经求出了了 [ R + 1 , n ] [R+1,n] [R+1,n] 这些数的单调栈。
当前段的右端点是 R R R,然后计算其左端点。
考虑左边来了一个段,其平均数为 x x x ,当前平均数为 p p p
- x > p x>p x>p
这时我们显然要把前面那个段给合并进来,那么当前的平均数就会变大。
由于 x x x 往前是单调不升的,所以肯定会在某一个地方停止合并。 - x < p x<p x<p
那么这个时候已经停止合并,只可能在后面某处停止合并。
所以左端点具有可二分性,可以线段树上二分求出。
然后考虑求右端点。
这个同样具有可二分性,因为当前段合法的话往后再并入一个后的平均值小于后面那一个段,就更加小于并入后的后继段了。
二分套二分求出左右端点就能算答案了。
code:
/*
1. 一个点最后会在的右端点一定是某个后缀单调栈节点的边界? 通过从后往前暴力的正确性可知
2. 二分的依据&正确性? 非情况讨论出往前加入一系列单调栈节点后平均值的变化,为一个单峰函数
*/
#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}typedef long long ll;
typedef double db;
const int N=1e5+10;
const int MAXN=N*80;
const int mod=998244353;
template<class T>inline void Inc(T&x,int y){
x+=y;if(x>=mod) x-=mod;}
template<class