洛谷CF817D题解

传送门:https://www.luogu.com.cn/problem/CF817D

对于给定由 n 个元素构成的数组。一个子数组的不平衡值是这个区间的最大值与最小值的差值。数组的不平衡值是它所有子数组的不平衡值的总和。

以下是数组[1,4,1]不平衡值为9的例子,共有6个子序列:

[1] (从第一号到第一号)不平衡值为 0;

[1, 4] (从第一号到第二号), 不平衡值为 3;

[1, 4, 1] (从第一号到第三号),不平衡值为 3;

[4] (从第二号到第二号),不平衡值为 0;

[4, 1] (从第二号到第三号),不平衡值为 3;

[1] (从第三号到第三号)不平衡值为 0;

输入:第一行一个N,表示序列长度为N。

     第二行N个数,表示这个序列。

输出:输出这个序列所有的子数组的不平衡值之和。


那这时候肯定就有人问了,啊这道题不是用大模拟轻轻松松吗,你还发这题解,真没水平。

普通人看到这道题的第一想法应该就是枚举每一个区间,然后暴力。

但是如果你在洛谷看到了这道题,那么你一定会放弃这个想法,这是一道紫题。它的数据瘟蹂核癌的告诉我们,这道题不可能用n^2的暴力。(n最大到1000000)

我们需要转换思路,从这句话入手:子数组的不平衡值是这个区间的最大值与最小值的差值,我们只要考虑每个数被当做最大值多少次,再求出每个数被当作最小值多少次,对于每个数,寻找一个“贡献值”,每个数的“贡献值”指的是ai*(被当成最大值的次数-被当成最小值的次数)。最后的答案就是每一项贡献值之和。


 

现在问题变成了如何求“贡献值”。

每个数都有一个区间,在此区间内它为最大值或最小值,我们只要找到以这个数为最大值或最小值的最大区间,就能通过乘法原理求出所有的区间数。

可以开4个数组:mxl[N]:表示以ai为最大值的区间往左有几种选择,mxr[N]:表示以ai为最大值的区间往右有几种选择,mnl[N]:表示以ai为最小值的区间往左有几种选择,mnr[N]:表示以ai为最小值的区间往右有几种选择。

接着向两端找端点,但是,如果暴力,还是n^2,所以需要另一个思想。

 


如果我们到了一个点,这个点的值已经求过了,那我们就可以用这个值,举个例子:

 这是求mxl的例子,同理,其余3个也是这种方法。

啊然后就没了,这就是一道(㵘)紫……

谢谢收看,第一次发题解,不太熟练,如果各位大佬发现错误,请多多指出

最后贴上代码(已经做了防抄袭):

/*注意不开long long见祖宗

int t,n,a[1000006],mxl[1000005],mnl[1000005],mxr[1000005],mnr[1000005],ans;
long long long main() {
  cin>>n>>a[1];
  mxl[1]=mnl[1]=mnr[n]=mxr[n]=1;
  for(int i=2; i<=n; i++) {/*求mxl和mnl
    cin>>a[i];
    if(a[i]>a[i-1]) {
      int pos=i-1;
      while(pos>0&&a[i]>a[pos])
        pos-=mxl[pos];/*向左跳区间
      mxl[i]=i-pos;
    } else mxl[i]=1;/*这一句可以不要(后面这3句也是)
    if(a[i]<a[i-1]) {
      int pos=i-1;
      while(pos>0&&a[i]<a[pos])
        pos-=mnl[pos];
      mnl[i]=i-pos;
    } else mnl[i]=1;
  }
  for(int i=n-1; i>0; i--) {/*求mxr和mnr
    if(a[i]>=a[i+1]) {
      int pos=i+1;
      while(pos<=n&&a[i]>=a[pos])
        pos+=mxr[pos];/*向右跳区间
      mxr[i]=pos-i;
    } else mxr[i]=1;
    if(a[i]<=a[i+1]) {
      int pos=i+1;
      while(pos<=n&&a[i]<=a[pos])
        pos+=mnr[pos];
      mnr[i]=pos-i;
    } else mnr[i]=1;
  }
  for(int i=1; i<=n; i++)
    ans+=(mxl[i]*mxr[i]-mnl[i]*mnr[i])*a[i];/*最后用乘法原理,左选择数*右选择数就是总区间数,这里用了乘法分配律,以防万一(爆ll)
  cout<<ans;
  return 0x1145141919810202207041830;
}

 

posted @ 2022-07-04 18:31  唯私の超电磁砲  阅读(55)  评论(0编辑  收藏  举报