「HNOI2019」序列
传送门
题解
终于看保序回归了吗
首先送了\(\text{50pts}\),直接用保序回归例题1的做法即可。
仔细分析一下,这个\(B\)就是\(\frac{\sum A}{\sum W}\),那么我们考虑分成若干段之后的答案是:
\[\begin{align}
\sum{(A_i-B)}^2&=\sum A_i^2 + \sum B^2- 2\sum A_iB
\\
&=\sum A_i^2+\sum w\frac{\sum A^2}{\sum w^2}-2\times\frac{\sum A}{\sum w}
\\
&=\sum A_i^2 -\frac{\sum A^2}{\sum w}
\end{align}
\]
所以只需要维护\(\sum A,\sum w\),前面这个\(A_i^2\)可以直接维护。
const int N=200010,Mod=998244353;
int n,m,A[N];
int qpow(int a,int b){int ret=1;while(b){if(b&1)ret=1ll*ret*a%Mod;b>>=1;a=1ll*a*a%Mod;}return ret;}
struct node
{
ll x,y;
bool operator<(const node &b)const{return 1ll*x*b.y<1ll*y*b.x;}
node operator+(const node &b)const{return (node){x+b.x,y+b.y};}
int calc(){return 1ll*(x%Mod)*(x%Mod)%Mod*qpow(y%Mod,Mod-2)%Mod;}
}sta[N];
int calc()
{
int ans=0,top=0;
for(int i=1;i<=n;i++)ans=(ans+1ll*A[i]*A[i]%Mod)%Mod;
for(int i=1;i<=n;i++)
{
node ret=(node){A[i],1};
while(top&&ret<sta[top]){ret=ret+sta[top];top--;}
sta[++top]=ret;
}
for(int i=1;i<=top;i++)ans=(ans-sta[i].calc()+Mod)%Mod;
return ans;
}
考虑\(100pts\),我们现在需要改变一个数字的值,然后维护这个栈。
不难想到可以选择左边的一个栈位置,右边的一个栈位置\((L0,R0)\),然后合并成一段。
那么考虑这个\((L0,R0)\)的需求是合并成一段后对于前后都合法,这一段刚好可以合并成一段。
也就是说对于一个\(R0\)我们要求一个最大的\(L0\),同时要求最大的\(R0\)。
所以我们可以二分\(R0\),然后二分\(L0\)即可。
代码
/*====================
author: fexuile
mail: fexuile@qq.com
QQ: 2165008534
====================*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
#include<set>
#include<map>
using namespace std;
#define mp make_pair
#define ll long long
#define re register
typedef pair<int,int> pii;
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi()
{
int f=1,sum=0;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const int N=200010,Mod=998244353;
int n,m,x[N];
int qpow(int a,int b){int ret=1;while(b){if(b&1)ret=1ll*ret*a%Mod;b>>=1;a=1ll*a*a%Mod;}return ret;}
struct node
{
ll x,y;
bool operator<(const node &b)const{return 1ll*x*b.y<1ll*y*b.x;}
node operator+(const node &b)const{return (node){x+b.x,y+b.y};}
int calc(){return 1ll*(x%Mod)*(x%Mod)%Mod*qpow(y%Mod,Mod-2)%Mod;}
}staA[N],staB[N];
int sA[N],sB[N],topA,topB,A[N],B[N],ans[N],ql,qr;
ll sum[N][2];
vector<pii>vec[N];
void getL(int delta,int R0)
{
int l=0,r=topA;
while(l<r)
{
int mid=(l+r+1)>>1;
if(staA[mid]<((node){sum[R0-1][0]-sum[A[mid]][0]+delta,R0-A[mid]-1}))l=mid;
else r=mid-1;
}
ql=l;
}
void getR(int delta)
{
int l=0,r=topB;
while(l<r)
{
int mid=(l+r+1)>>1;getL(delta,B[mid]);
if(((node){sum[B[mid]-1][0]-sum[A[ql]][0]+delta,B[mid]-A[ql]-1})<staB[mid])l=mid;
else r=mid-1;
}
qr=l;getL(delta,B[qr]);
}
int solve(int delta)
{
getR(delta);
// printf("%d %d %d\n",A[ql],B[qr],delta);
return ((ll)sA[ql]+sB[qr]+((node){sum[B[qr]-1][0]-sum[A[ql]][0]+delta,B[qr]-A[ql]-1}).calc())%Mod;
}
vector<int>del[N];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
n=gi();m=gi();
for(int i=1;i<=n;i++)x[i]=gi(),sum[i][0]=sum[i-1][0]+x[i],sum[i][1]=(sum[i-1][1]+1ll*x[i]*x[i]%Mod)%Mod;
for(int i=1;i<=m;i++)
{
int p=gi();
vec[p].push_back(mp(gi()-x[p],i));
}
B[0]=n+1;
for(int i=n;i;i--)
{
node ret=(node){x[i],1};
while(topB&&staB[topB]<ret)
{
del[i].push_back(B[topB]);
ret=ret+staB[topB];topB--;
}
staB[++topB]=ret;sB[topB]=(sB[topB-1]+ret.calc())%Mod;B[topB]=i;
}
printf("%d\n",(sum[n][1]-sB[topB]+Mod)%Mod);
for(int i=1;i<=n;i++)
{
topB--;
for(int j=del[i].size()-1;~j;j--)
{
B[++topB]=del[i][j];
staB[topB]=(node){sum[B[topB-1]-1][0]-sum[B[topB]-1][0],B[topB-1]-B[topB]};
sB[topB]=(sB[topB-1]+staB[topB].calc())%Mod;
}
int res=(sum[n][1]-1ll*x[i]*x[i]%Mod+Mod)%Mod;
for(auto id:vec[i])
ans[id.second]=((res+1ll*(x[i]+id.first)*(x[i]+id.first)%Mod)%Mod-solve(id.first)+Mod)%Mod;
node ret=(node){x[i],1};
while(topA&&ret<staA[topA]){ret=ret+staA[topA];topA--;}
staA[++topA]=ret;sA[topA]=(sA[topA-1]+ret.calc())%Mod;A[topA]=i;
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}