「HNOI2019」序列

传送门

Luogu

题解

终于看保序回归了吗

首先送了\(\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;
}
posted @ 2020-06-27 07:47  fexuile  阅读(168)  评论(2编辑  收藏  举报