1. 程式人生 > 其它 >#保序迴歸問題,單調棧,二分#洛谷 5294 [HNOI2019]序列

#保序迴歸問題,單調棧,二分#洛谷 5294 [HNOI2019]序列

保序迴歸問題,單調棧,二分

題目

給定一個長度為 \(n\) 的序列 \(A\),以及 \(m\) 個操作,每個操作將一個 \(A_i\) 修改為 \(k\)

第一次修改之前及每次修改之後,都要求你找到一個同樣長度為 \(n\)單調不降序列 \(B\)

使得 \(\sum_{i=1}^n(A_i-B_i)^2\) 最小,並輸出最小值。

需要注意的是每次操作的影響都是獨立的,也即每次操作只會對當前詢問造成影響。

\(n\leq 10^5,A\leq 10^9\)


分析

對於形如最小化 \(\sum C_i(A_i-m)^2\) 的問題,\(m\) 應取 \(\large \frac{\sum C_iA_i}{\sum C_i}\)

而且這個序列 \(B\) 等同於用單調遞增的 \(m\) 去覆蓋,這種問題可以用單調棧維護均值。

考慮比較獨立的修改可以離線做,此時 \([1,x)\)\((x,n]\) 的單調棧都可以求出來。

反向的單調棧原理相同,先倒著做一遍再把出入棧撤銷。

修改的位置會修改中間一段,兩邊依舊是原來的單調棧,

所以可以二分右端點,其位置越靠前,答案越小;

當右端點二分的情況下,再二分左端點,其位置越靠後答案越小。

時間複雜度 \(O(n\log ^2 n)\)


程式碼

#include <cstdio>
#include <cctype>
#include <stack>
#define rr register
using namespace std;
const int N=100011,mod=998244353;
typedef long long lll; lll s[N];
struct node{int y,next;}q[N]; stack<int>K[N]; 
int wl[N],wr[N],a[N],_s[N],n,Q,as[N],Tol,Tor,stal[N],star[N],ans[N];
inline signed iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
} 
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline signed o(int x){return 1ll*x*x%mod;}
inline signed ksm(int x,int y){
	rr int ans=1;
	for (;y;y>>=1,x=1ll*x*x%mod)
	    if (y&1) ans=1ll*ans*x%mod;
	return ans;
}
struct rec{
	lll x; int y;
	inline bool operator <=(const rec &t)const{
		return (double)x*t.y<=(double)t.x*y;
	}
	inline signed ans(){return 1ll*o(x%mod)*ksm(y,mod-2)%mod;}
}cl[N],cr[N];
inline rec slope(int l,int r){return (rec){s[r]-s[l-1],r-l+1};} 
inline signed Get(int x,int now){
	rr int l=0,r=Tol;
	while (l<r){
		rr int mid=(l+r+1)>>1;
		if (cl[mid]<=(rec){s[x-1]-s[stal[mid]]+now,x-1-stal[mid]}) l=mid;
		    else r=mid-1;
	}
	return l;
}
inline signed query(int now){
	rr int l=0,r=Tor,mid,_l;
	while (l<r){
		_l=Get(star[mid=(l+r+1)>>1],now);
		if ((rec){s[star[mid]-1]-s[stal[_l]]+now,star[mid]-1-stal[_l]}<=cr[mid]) l=mid;
		    else r=mid-1;
	}
	_l=Get(star[r],now);
	return mo(mo(wl[_l],wr[r]),(rec){s[star[r]-1]-s[stal[_l]]+now,star[r]-stal[_l]-1}.ans());
}
signed main(){
	n=iut(),Q=iut(),star[0]=n+1;
	for (rr int i=1;i<=n;++i) s[i]=s[i-1]+(a[i]=iut());
	for (rr int i=1;i<=n;++i) _s[i]=mo(_s[i-1],o(a[i]));
	for (rr int i=1;i<=Q;++i){
		rr int x=iut(),y=iut();
		q[i]=(node){y-a[x],as[x]},as[x]=i;
	}
	for (rr int i=n;i;--i){
		for (;Tor&&cr[Tor]<=slope(i,star[Tor]-1);K[i].push(star[Tor--]));
		star[++Tor]=i,wr[Tor]=mo(wr[Tor-1],(cr[Tor]=slope(i,star[Tor-1]-1)).ans());
	}
	print(mo(_s[n],mod-wr[Tor]));
	for (rr int i=1;i<=n;++i){
		--Tor;
		for (;!K[i].empty();K[i].pop())
		    star[++Tor]=K[i].top(),wr[Tor]=mo(wr[Tor-1],(cr[Tor]=slope(star[Tor],star[Tor-1]-1)).ans());
		rr int now=mo(_s[n],mod-o(a[i]));
		for (rr int j=as[i];j;j=q[j].next)
		    ans[j]=mo(now,mo(o(a[i]+q[j].y),mod-query(q[j].y)));
		for (;Tol&&slope(stal[Tol]+1,i)<=cl[Tol];--Tol);
		stal[++Tol]=i,wl[Tol]=mo(wl[Tol-1],(cl[Tol]=slope(stal[Tol-1]+1,i)).ans());
	}
	for (rr int i=1;i<=Q;++i) putchar(10),print(ans[i]);
	return 0;
}