[CodeCraft-20 (Div. 2)][Codeforces 1316F. Battalion Strength]
题目链接:1316F - Battalion Strength
题目大意:对于一个序列\(\left \{ a_n \right \}\),定义其权值为,将其排序后,\(\sum_{i=1}^{n-1}a_i\cdot a_{i+1}\)的值。现在给出一个数组,要求每次修改其中一个位置的值,并求出修改后随机选取一个子序列的权值的期望
题解:先考虑对于一个有序数组\(\left \{ a_n \right \}\)对应的答案是多少
对于任一一对满足\(1\le i < j \le n\)的\((i,j)\),能够让\(a_i\)与\(a_j\)相邻的子序列个数为\(2^{n-(j-i+1)}\),因此我们可以得出最开始的式子
$$ans=\frac{\sum_{i=1}^n\sum_{j=i+1}^n a_i\cdot a_j \cdot 2^{n-j+i-1}}{2^n}$$
把分母消掉后得出
$$ans=\sum_{i=1}^n\sum_{j=i+1}^n a_i\cdot a_j \cdot 2^{i-j-1}$$
因此如果没有修改操作,我们就可以直接把原数组进行排序后,用分治的思想(线段树)求出答案
对于有修改的情况,我们可以把修改的值加进来一起排序,这样就可以通过用线段树维护目前有哪些数存在来得出答案。对于每个结点,我们维护对应区间内有值的位置个数\(c\),对应的答案\(ans\),这个区间在左边或右边时需要乘上的系数\(sl,sr\)
而对于这个系数,我们可以通过回顾之前的那个式子发现,每对\((i,j)\)的贡献是\(2^{i-j-1}\)。这时如果直接分别维护\(a_i\cdot 2^i\)和\(a_i\cdot 2^{-i}\)的值,在合并时会出现不小的麻烦。因为假设左区间有\(n_l\)个位置有值 ,其第\(i\)个元素\(l_i\)与右区间的第\(j\)个元素\(r_j\)合并时。我们会发现在新区间中,\(r_j\)不再是区间的第\(j\)个元素了,而是第\(n_l+j\)个。但是我们又可以发现,在新的区间里,他们的贡献变成了\(2^{i-(n_l+j)+1}=2^{i-n_l-j-1}=2^{-(n_l-i+1)}\cdot 2^{-j}\)。这样的话我们对于一个区间内的数,只需令\(sl=\sum_{i=1}^{c}b_i\cdot 2^{-(c-i+1)},sr=\sum_{i=1}^{c}b_i\cdot 2^{-i}\)即可,其中\(b_i\)为区间内第\(i\)个非空位置对应的值
这样我们就可以排序后,先把每个\(\left \{ a_n \right \}\)对应的位置设为有值的状态,之后每次询问只需把对应修改的位置置零,新值对应的位置设为有值即可,注意修改后由于\(a_i\)的值发生了改变,其对应的位置也要进行变化
#include<bits/stdc++.h> using namespace std; #define N 600001 #define MOD 1000000007 int n,m,p[N],q[N],I[N],v; struct pi { int v,id; void read(int i){scanf("%d",&v),id=i;} bool operator <(const pi &t)const{return v<t.v;} }a[N]; struct rua { int c,ans,sl,sr; }t[N<<2]; void change(int x,int i,int l,int r,int o) { if(l==r) { t[i].c=o; t[i].ans=t[i].sl=t[i].sr=0; if(o)t[i].sl=t[i].sr=1ll*I[1]*a[x].v%MOD; return; } int mid=l+r>>1,ls=i*2,rs=ls+1; if(x<=mid)change(x,ls,l,mid,o); else change(x,rs,mid+1,r,o); t[i].c=t[ls].c+t[rs].c; t[i].sl=(1ll*t[ls].sl*I[t[rs].c]+t[rs].sl)%MOD; t[i].sr=(1ll*t[rs].sr*I[t[ls].c]+t[ls].sr)%MOD; t[i].ans=(t[ls].ans+t[rs].ans+1ll*t[ls].sl*t[rs].sr)%MOD; } int main() { I[0]=1,I[1]=(MOD+1)/2; for(int i=2;i<N;i++) I[i]=1ll*I[1]*I[i-1]%MOD; scanf("%d",&n); for(int i=1;i<=n;i++) a[i].read(i),p[i]=i; scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d",&q[i]); a[n+i].read(n+i); p[n+i]=n+i; } sort(a+1,a+n+m+1); for(int i=1;i<=n+m;i++) p[a[i].id]=i; for(int i=1;i<=n;i++) change(p[i],1,1,n+m,1); printf("%d\n",t[1].ans); for(int i=1;i<=m;i++) { change(p[q[i]],1,1,n+m,0); change(p[n+i],1,1,n+m,1),p[q[i]]=p[n+i]; printf("%d\n",t[1].ans); } }