zoukankan      html  css  js  c++  java
  • Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列

    给定一个长度为 (n) 的序列 (A_1, ldots , A_n),以及 (m) 个操作,每个操作将一个 (A_i) 修改为 (k)。第一次修改之前及每次修改之后,都要求你找到一个同样长度为 (n) 的单调不降序列 (B_1, ldots , B_n),使得 (sum_{i=1}^n (A_i −B_i)^2) 最小,并输出该最小值。需要注意的是每次操作的影响都是独立的,也即每次操作只会对当前询问造成影响。为了避免精度问题,我们保证这个最小值是个分数,也即能表示为两个非负整数相除的形式:(x/y)。那么你将要输出 ((x imes y^{P-2})mod P) 的值,表示模意义下 (x/y) 的值。其中 (P=998244353) 是一个大质数。

    输入格式

    第一行两个非负整数 (n,m),代表序列长度和操作数。

    第二行有 (n) 个由空格隔开的正整数,代表序列 (A_1, ldots , A_n)

    接下来 (m) 行每行两个正整数 (i, k),代表将 (A_i) 修改为 (k)

    输出格式

    输出 (m + 1) 行每行一个整数,第 (i) 行输出第 (i − 1) 次修改后的答案。特别的,第 (1) 行应为初始局面的答案。

    数据范围与提示

    对于前 (10\%) 的数据,保证 (n, m le 10)(k, A_i ≤ 1000),且存在一种最优方案,使得 (B_i) 皆为整数。

    对于前 (30\%) 的数据,保证 (n, m le 100)

    对于另外 (20\%) 的数据,保证 (m = 0)

    对于另外 (20\%) 的数据,保证 (n, m le 3 imes 10^4)

    对于所有数据,保证 (3 le n le 10^5, 0 le m le 10^5, 1 le k, A_i le 10^9)

    (\)

    Orz

    假设没有修改,那这就是个经典问题(然而我不会)

    如果(A)也是个单调不下降序列,那么答案就是(0)。否则,对于(A_i>A_{i+1})的情况,我们将(A_i)(A_{i+1})合并起来。合并后的块之间也存在这种关系,不过是拿块的平均值比较。

    (B_i)就是(i)所在块的平均值。

    我们先将询问离线下来,按位置排序。假设当前处理位置(i)的询问,我们维护两个栈,一个是从(1)(i-1),按上述规则合并后的栈;一个数从(i+1)(n)按上述规则合并后的栈。对于后一个栈,我们可以先从(n)(1)维护一遍,记录下每个位置(i)加入栈中对这个栈的修改。然后依次回退。

    假设修改后(i)所在的块为([L,R]),那么([L,R])([1,L-1])([R+1,n])分别形成的栈之间是相互独立的。这也就是维护两个栈的原因。

    考虑求([L,R])。我们先二分出(R),然后再找对应的(L),判断([L,R])的平均值是否(leq)(R+1)所在块的平均值,如果是,将二分边界往左移;否则往右移(注意这里二分的是完整的块)。因为如果以(R)所在块为右端点成立,那么我们加入(R)所在块右侧的块依然成立,所以答案是有单调性的。找(R)对应的(L)用的也是相同的原理。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 100005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    const ll mod=998244353;
    ll ksm(ll t,ll x) {
    	ll ans=1;
    	for(;x;x>>=1,t=t*t%mod)
    		if(x&1) ans=ans*t%mod;
    	return ans;
    }
    
    int n,m;
    ll a[N];
    
    struct node {
    	ll sum,size;
    	int l,r;
    	long double key;
    	node() {}
    	node(ll _sum,ll _size,int _l,int _r) {
    		sum=_sum,size=_size;
    		l=_l,r=_r;
    		key=(long double)sum/size;
    	}
    };
    
    node operator +(const node &a,const node &b) {return node(a.sum+b.sum,a.size+b.size,min(a.l,b.l),max(a.r,b.r));}
    
    int cal() {
    	static node st[N];
    	int top;
    	st[top=1]=node(a[1],1,1,1);
    	node tem;
    	for(int i=2;i<=n;i++) {
    		tem=node(a[i],1,i,i);
    		while(top>=1&&st[top].key>=tem.key) {
    			tem=tem+st[top];
    			top--;
    		}
    		st[++top]=tem;
    	}
    	int now=1;
    	ll ans=0;
    	for(int i=1;i<=top;i++) {
    		ll ave=st[i].sum%mod*ksm(st[i].size,mod-2)%mod;
    		for(int j=1;j<=st[i].size;j++,now++) {
    			(ans+=(a[now]-ave)*(a[now]-ave))%=mod;
    		}
    	}
    	return ans;
    }
    
    int rx[N],lx[N];
    ll sum[N];
    ll suf[N],pre[N],sums[N];
    
    int t1=0,t2=0;
    node pres[N],sufs[N];
    int pos[N];
    node del[N];
    
    struct query {
    	int id,k;
    };
    
    vector<query>q[N];
    int Find_left(int x,ll val,int R) {
    	if(x==1) return 1;
    	if(pres[t1].key<=(long double)(sum[R]-sum[x]+val)/(R-x+1)) return x;
    	int l=1,r=t1,mid;
    	while(l<r) {
    		mid=l+r+1>>1;
    		int L=pres[mid].l;
    		long double k=(long double)(sum[R]-sum[L-1]-a[x]+val)/(R-L+1);
    		if(pres[mid-1].key<=k) l=mid;
    		else r=mid-1;
    	}
    	return pres[l].l;
    }
    
    int Find_right(int x,ll val) {
    	if(x==n) return n;
    	int L=Find_left(x,val,x);
    	if(sufs[t2].key>=(long double)(sum[x-1]-sum[L-1]+val)/(x-L+1)) return x;
    	int l=1,r=t2,mid;
    	while(l<r) {
    		mid=l+r+1>>1;
    		int R=sufs[mid].r;
    		int L=Find_left(x,val,R);
    		long double k=(long double)(sum[R]-sum[L-1]-a[x]+val)/(R-L+1);
    		if(k>sufs[mid-1].key) r=mid-1;
    		else l=mid;
    	}
    	return sufs[l].r;
    }
    
    ll Ans[N];
    
    int main() {
    	n=Get(),m=Get();
    	for(int i=1;i<=n;i++) a[i]=Get();
    	
    	for(int i=1;i<=n;i++) {
    		sum[i]=sum[i-1]+a[i];
    		sums[i]=(sums[i-1]+a[i]*a[i])%mod;
    	}
    	cout<<cal()<<"
    ";
    	
    	for(int i=1;i<=m;i++) {
    		int x=Get(),k=Get();
    		q[x].push_back((query) {i,k});
    	}
    	
    	node tem;
    	for(int i=n;i>=1;i--) {
    		tem=node(a[i],1,i,i);
    		pos[i]=t2;
    		while(t2&&sufs[t2].key<=tem.key) {
    			tem=tem+sufs[t2];
    			t2--;
    		}
    		del[i]=sufs[++t2];
    		sufs[t2]=tem;
    		
    		ll ave=tem.sum%mod*ksm(tem.size,mod-2)%mod;
    		int R=tem.r;
    		suf[i]=((suf[R+1]+sums[R]-sums[i-1]+ave*ave%mod*(R-i+1)-2*ave*((sum[R]-sum[i-1])%mod))%mod+mod)%mod;
    	}
    	
    	pres[0].key=0;
    	sufs[0].key=1e9+7;
    	for(int i=1;i<=n;i++) {
    		sufs[t2]=del[i];
    		t2=pos[i];
    		for(int j=0;j<q[i].size();j++) {
    			int x=i;
    			ll k=q[i][j].k;
    			ll dlt=(k-a[x]+mod)%mod,dlts=(k*k-a[x]*a[x])%mod+mod;
    			int R=Find_right(x,k),L=Find_left(i,k,R);
    			ll ans=(pre[L-1]+suf[R+1])%mod;
    			ll ave=(sum[R]-sum[L-1]+dlt+mod)%mod*ksm(R-L+1,mod-2)%mod;
    			ans=((ans +sums[R]-sums[L-1]+dlts +ave*ave%mod*(R-L+1)%mod -2*ave*((sum[R]-sum[L-1]+dlt)%mod))%mod+mod)%mod;
    			Ans[q[i][j].id]=ans;
    		}
    		tem=node(a[i],1,i,i);
    		while(t1&&pres[t1].key>=tem.key) {
    			tem=tem+pres[t1];
    			t1--;
    		}
    		pres[++t1]=tem;
    		
    		ll ave=tem.sum%mod*ksm(tem.size,mod-2)%mod;
    		int L=tem.l;
    		pre[i]=((pre[L-1]+sums[i]-sums[L-1]+ave*ave%mod*(i-L+1)%mod-2*ave*((sum[i]-sum[L-1])%mod))%mod+mod)%mod;
    	}
    	for(int i=1;i<=m;i++) cout<<Ans[i]<<"
    ";
    	return 0;
    }
    
    
  • 相关阅读:
    我不喜欢的 Rust 特性 (之一) eager drop
    为 Windows Phone 8.1 app 解决“The type does not support direct content.”的问题
    输入10个互不相同的数字并分成5对,问有多少种分法。
    code wars quiz: toInteger
    my first emacs custom key binding
    http协议消息报头学习笔记
    移动端经常遇到的小bug
    js小技巧
    ajax
    js正则表达
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10702421.html
Copyright © 2011-2022 走看看