题解 CF817D 【Imbalanced Array】

Algha_Porthos

2020-02-22 02:25:02

Solution

首先,Orz _DXL\_ . 然后冷静分析,发现这是一道比较玄学的题目。 **题目实际上是给定一个数组,求每一个区间的最大值最小值的差的总和。** ~~然后$\Theta(N^2logN)$水过。~~ 对于1e6级别的数据,我们需要另辟蹊径:**求贡献**。 我们可以发现,“求(每一个区间的最大值最小值的差)的总和”(括号代表断句), 可以转化为“求(每一个区间的最大值的总和)与(每一个区间的最小值的总和)的差”, 又可以转化为“求每一个数字作为最大值和最小值的次数,两者乘上权,再求差,再求和”。 然后问题就迎刃而解。 进入代码部分。 这道题是用单调栈来求贡献。这是一个相当经典的模型。 ![pic1](http://47.103.204.220/picture_bed/uploads/2020/02/CF817D1.png) 假如每个竖的线段的高度代表每一个数值,我们当前要求最大值得总和。那么我们需要考虑红的和绿的分别会作为哪些区间的最大值。 红:(2,2) 绿:(2,4)(3,4)(4,4)(2,5)(3,5)(4,5) 我们发现,作为区间最大值的区间数量其实就是 到左边“靠山”的距离 乘上 到右边的“靠山”的距离。 ![pic2](http://47.103.204.220/picture_bed/uploads/2020/02/CF817D2.png) 读者自证不难。同时,对于求最小值总和,由于不方便形象举例,读者自行推导(反正就是大小于号换一下)。 ## 代码 加一的细节卡了我好久,希望大家注意一下。 需要注意的是,如果所有元素都遍历完了,还有元素在单调栈里,我们需要在$n+1$的位置放一个“巨型靠山”,否则会出错。 ``` #include<bits/stdc++.h> #define int long long using namespace std; const int N=1050005; int n,st[N],a[N],b[N],pos[N],top=0; int ans=0; int sum_max,sum_min; int Left[N],Right[N]; signed main(){ cin>>n; for(int i=1;i<=n;++i) scanf("%lld",&a[i]); //--------------以下是计算最大值贡献-------------- for(int i=1;i<=n;++i){ while(top!=0&&st[top]<a[i]){//单调栈的维护。st[]表示单调栈,存放元素的具体数值。top表示顶指针。 Right[pos[top]]=i-pos[top];//Right[]是右边的靠山。pos[i]表示st[i]的元素在a[]数组中的下标。 top--; } Left[i]=i-pos[top];//Left[]是左边的靠山。 st[++top]=a[i];//元素入栈 pos[top]=i; } while(top)//栈内剩余元素处理(巨型靠山) Right[pos[top]]=n+1-pos[top],top--; for(int i=1;i<=n;++i)//计算贡献 sum_max+=(a[i]*(Left[i])*(Right[i])); //-----以下是计算最小值贡献(就minmax换一下,改个大小于号。具体的参考上面。 memset(Left,0,sizeof(Left)); memset(Right,0,sizeof(Right)); for(int i=1;i<=n;++i){ while(top!=0&&st[top]>a[i]){ Right[pos[top]]=i-pos[top]; top--; } Left[i]=i-pos[top]; st[++top]=a[i]; pos[top]=i; } while(top) Right[pos[top]]=n+1-pos[top],top--; for(int i=1;i<=n;++i) sum_min+=(a[i]*(Left[i])*(Right[i])); cout<<sum_max-sum_min<<endl;//输出 } ``` 注释都在上面了。有问题可以博客提问,或者私信提问! ## 鸣谢 感谢洛谷提供评测和博客服务。 感谢ISP项目组提供私人图床服务。