题解:[ARC126E] Infinite Operations

题目传送门

题意分析

考虑极限 \(\displaystyle\lim_{n\rightarrow\infty}f(n)\) 的取值。显然是一个可以由 \(a_1,a_2,\cdots,a_n\) 确定的取值。

答案与 \(a_1,a_2,\cdots,a_n\) 顺序无关,不妨钦定 \(a_1\leq a_2\leq\cdots\leq a_n\)。

记选择 \(a_i+2x\leq a_j\) 做一次操作为 \((i,j,x)\)。

考虑单次操作 \((i,j,x)\) 显然存在一种最优策略,考虑什么样是最优的。

钦定 \(x,j\) 不变,如果先操作 \((j-1,j,x)\),可以获得 \(x\) 的贡献,且 \(i\sim j-1\) 之间还有可能继续贡献,因此对于确定的 \(i,j,x\) 这样最优。

因而单次最优操作一定是相邻的操作,又因为最终所有数都会变成平均数 \(\displaystyle\overline a=\dfrac1n\sum_{i=1}^na_i\),考虑拆贡献。

将一次操作视为从后往前转移的,\((i-1,i,x)\) 操作的 \(x\) 之和即为 \(a_i\sim a_n\) 减量的总和,即 \(\displaystyle\sum_{j=i}^n\left(a_j-\overline a\right)\)。

最终单次询问答案为:

\[\begin{aligned} \sum_{i=1}^n\sum_{j=i}^n\left(a_j-\overline a\right)&=\sum_{i=1}^ni\cdot a_i-\dfrac{n+1}2\sum_{i=1}^na_i \end{aligned} \]

考虑到我们钦定了 \(a_1\leq a_2\leq\cdots\leq a_n\),实际过程中不能如此,记 \(\operatorname{rank}(i)\) 为 \(a_i\) 的排名(相同元素的排名不同),有答案为:

\[\sum_{i=1}^n\operatorname{rank}(i)\cdot a_i-\dfrac{n+1}2\sum_{i=1}^na_i \]

平衡树维护序列即可。考虑左子树对于右子树和根的排名的贡献为左子树大小即可维护。

具体而言,考虑如何维护节点 \(p\) 的信息,记其左右子节点分别为 \(\operatorname{lChild}(p),\operatorname{rChild}(p)\)。

对于节点 \(x\),记子树大小为 \(\operatorname{size}(x)\),权值为 \(\operatorname{value}(x)\),子树权值和为 \(\operatorname{sum}(x)\),答案为 \(\operatorname{ans}(x)\),单次询问答案即 \(\operatorname{ans}(\textit{root})\)。

则更新节点 \(\operatorname{ans}(p)\) 即:

\[\operatorname{ans}(p)=\operatorname{ans}(\operatorname{lChild}(p))+\operatorname{ans}(\operatorname{rChild}(p))+(\operatorname{size}(\operatorname{lChild}(p))+1)(\operatorname{value}(p)+\operatorname{sum}(\operatorname{rChild}(p))) \]

其实就是考虑对排名拆贡献,左子树对于右子树和根的排名的贡献都是左子树大小。

AC 代码

//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#include<random>
using namespace std;
typedef long long ll;
constexpr const int N=3e5,Q=3e5,V=1e9,P=998244353,inv2=499122177;
mt19937 Rand(time(0));
int n,a[N+1];
int root;
struct FHQTreap{
	int size;
	struct node{
		int value,sum,ans,size,rand;
		int lChild,rChild;
	}t[N+Q+1];
	
	FHQTreap(){
		size=root=0;
	}
	int create(int x){
		t[++size]={x,x%P,x%P,1,Rand()};
		return size;
	}
	void up(int p){
		t[p].size=t[t[p].lChild].size+t[t[p].rChild].size+1;
		t[p].sum=(1ll*t[t[p].lChild].sum+t[p].value+t[t[p].rChild].sum)%P;
		t[p].ans=(1ll*t[t[p].lChild].ans+t[t[p].rChild].ans+(t[t[p].lChild].size+1ll)*(t[p].value+t[t[p].rChild].sum)%P)%P;
	}
	void split(int p,int x,int &l,int &r){
		if(!p){
			l=r=0;
			return;
		}
		if(t[p].value<=x){
			l=p;
			split(t[p].rChild,x,t[p].rChild,r);
		}else{
			r=p;
			split(t[p].lChild,x,l,t[r].lChild);
		}
		up(p);
	}
	int merge(int l,int r){
		if(!l||!r){
			return l|r;
		}
		if(t[l].rand<t[r].rand){
			t[l].rChild=merge(t[l].rChild,r);
			up(l);
			return l;
		}else{
			t[r].lChild=merge(l,t[r].lChild);
			up(r);
			return r;
		}
	}
	void insert(int x){
		int l,r,p;
		split(root,x,l,r);
		root=merge(merge(l,create(x)),r);
	}
	void erase(int x){
		int l,r,p;
		split(root,x,l,r);
		split(l,x-1,l,p);
		p=merge(t[p].lChild,t[p].rChild);
		root=merge(merge(l,p),r);
	}
	int sum(){
		return t[root].sum;
	}
	int ans(){
		return t[root].ans;
	}
}t;
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int q,sum=0;
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		t.insert(a[i]);
	}
	while(q--){
		int x,y;
		cin>>x>>y;
		t.erase(a[x]);
		a[x]=y;
		t.insert(a[x]);
		int ans=(t.ans()-(n+1ll)*inv2%P*t.sum()%P)%P;
		if(ans<0){
			ans+=P;
		}
		cout<<ans<<'\n';
	}
	
	cout.flush(); 
	
	/*fclose(stdin);114514
	fclose(stdout);*/
	return 0;
}
posted @ 2026-09-24 21:56  TH911  阅读(6)  评论(0)    收藏  举报