[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);
      }
}
View Code

 

posted @ 2020-03-05 17:02  DeaphetS  阅读(360)  评论(0编辑  收藏  举报