题目链接:1316F - Battalion Strength
题目大意:对于一个序列(left { a_n ight }),定义其权值为,将其排序后,(sum_{i=1}^{n-1}a_icdot a_{i+1})的值。现在给出一个数组,要求每次修改其中一个位置的值,并求出修改后随机选取一个子序列的权值的期望
题解:先考虑对于一个有序数组(left { a_n ight })对应的答案是多少
对于任一一对满足(1le i < j le n)的((i,j)),能够让(a_i)与(a_j)相邻的子序列个数为(2^{n-(j-i+1)}),因此我们可以得出最开始的式子
$$ans=frac{sum_{i=1}^nsum_{j=i+1}^n a_icdot a_j cdot 2^{n-j+i-1}}{2^n}$$
把分母消掉后得出
$$ans=sum_{i=1}^nsum_{j=i+1}^n a_icdot a_j cdot 2^{i-j-1}$$
因此如果没有修改操作,我们就可以直接把原数组进行排序后,用分治的思想(线段树)求出答案
对于有修改的情况,我们可以把修改的值加进来一起排序,这样就可以通过用线段树维护目前有哪些数存在来得出答案。对于每个结点,我们维护对应区间内有值的位置个数(c),对应的答案(ans),这个区间在左边或右边时需要乘上的系数(sl,sr)
而对于这个系数,我们可以通过回顾之前的那个式子发现,每对((i,j))的贡献是(2^{i-j-1})。这时如果直接分别维护(a_icdot 2^i)和(a_icdot 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_icdot 2^{-(c-i+1)},sr=sum_{i=1}^{c}b_icdot 2^{-i})即可,其中(b_i)为区间内第(i)个非空位置对应的值
这样我们就可以排序后,先把每个(left { a_n ight })对应的位置设为有值的状态,之后每次询问只需把对应修改的位置置零,新值对应的位置设为有值即可,注意修改后由于(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 ",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 ",t[1].ans); } }