【题解】Norma [COCI2014] [SP22343]

传送门:\(\text{Norma [COCI2014] [P5899]}\) \(\text{[SP22343]}\)

【题目描述】

给定一个整数 \(n\) 和一个长度为 \(n\) 的序列 \(a\),子序列是指原序列中一段连续的序列。子序列的价值定义为它们中的最小值乘以最大值再乘以该子序列长度 。现要计算所有子序列的价值之和,答案对 \(1e9\) 取模。

【样例】

样例输入:
3
1
2
样例输出:
16 样例输入:
4
2
4
1
4
样例输出:
109 样例输入:
6
8
1
3
9
7
4
样例输出:
1042

【数据范围】

\(40 \%:\) \(1 \leqslant n \leqslant 5000\)

\(100 \%:\) \(1 \leqslant n \leqslant 5*10^5,\) \(1 \leqslant a[i] \leqslant 10^8\)


【分析】

询问过于奇葩,万能的线段树都没法搞,单调队列也许可做,但太复杂了。

可以用类似 \(\text{CDQ}\) 的思想递归分治:将一段区间分为左右两半,计算其中一半对另一半的贡献,得到另一半的答案。

对于一个区间 \([L,R]\),求出其中穿过了 \(a[mid]\) 的所有子序列价值总和,然后再递归求解 \([L,mid]\) 以及 \([mid\!+\!1,R]\),可以保证子序列的计算一定不重不漏。

考虑处理一个区间 \([L,R]\) ,先枚举 \(i \in [L,mid]\) 固定左端点,求出以每个 \(i\) 作为左端点的所有子序列贡献和。

对于每个 \(i\):

用 \(mi,mx\) 分别表示 \(min \{ a[i] \cdots a[mid] \}\) 和 \(max \{ a[i] \cdots a[mid] \}\)

设两个指针 \(j,k\),

\(j\) 表示满足 \(mi \leqslant min \{ a[mid\!+\!1] \cdots a[j] \}\) 的最大的 \(j\) 的位置,

\(k\) 表示满足 \(mx \geqslant max \{ a[mid\!+\!1] \cdots a[k] \}\) 的最大的 \(k\) 的位置。

设 \(w_{1}=min\{j,k\},w_{2}=max\{j,k\}\),此时右半部分被分成了三个部分,分别对其求解。

\((1).\) 完全满足 \(mi,mx\) 的部分: \([mid\!+\!1,w_{1}]\)

可知这一整段的元素数值范围都在 \([mi,mx]\) 以内,因此右端点在取 \([mid\!+\!1,w_{1}]\) 中的任意一个位置时,子序列最小值都始终为 \(mi\),最大值也始终为 \(mx\),至于区间长度,直接套等差数列公式就好了,小学数学不再赘述(这玩意儿有个很高大上的名字:高斯求和)。

\(1\) 部分对左端点 \(i\) 的总贡献可表示为: \(ans_{1}[i]=mi*mx*\frac{(((mid\!+\!1)\!-i\!+\!1)+(w1\!-\!i\!+\!1))*(w1\!-\!(mid\!+\!1)\!+\!1)}{2}\) 。

\((2).\) 满足 \(mi,mx\) 其一的部分: \([w_{1}\!+\!1,w_{2}]\)

这部分比较难想,在草稿纸上比划了好久才搞出来,而且还不太好描述。

分为 \(j<k\) \((\) 即 \(w_{1}\!<\!k\!=\!w_{2})\) 和 \(j>k\) \((\) 即 \(w_{1}\!<\!j\!=\!w_{2})\) 两种情况讨论。

以 \(j<k\) 为例,此时要计算右端点 \([w_{1}\!+\!1,w_{2}]\) 对 \(i\) 的贡献可以直接用 \(mx\),但 \(mi\) 不行,要用两个前缀和数组预处理一下这个东西。

由于子序列长度在不断的变化,但 \(mid+1\) 是始终不变的,可以轻松处理出左端点为 \(mid+1\) 的子序列贡献和,记为 \(S_{1}\) 。再看左边部分没有计算的部分长度,对于每一个 \(i\),它也是固定的 \((mid-i+1)\),于是还要用一个数组 \(S’_{1}\) 记录每个前缀最小值的前缀和。

这个东西不太好描述,见下面的式子:

\(S_{1}[x]\) \((mid\!+\!1\!\leqslant\!x\!\leqslant\!R)\) \(=\) \(\sum_{i=mid+1}^{x} ((i\!-\!(mid\!+\!1)\!+\!1)*max\{a[mid\!+\!1] \cdots a[i]\})\)

\(S’_{1}[x]\) \((mid\!+\!1\!\leqslant\!x\!\leqslant\!R)\) \(=\) \(\sum_{i=mid+1}^{x} max\{a[mid\!+\!1] \cdots a[i]\}\)

该做法的正确性来自于:随着右端点的递增,\(mid\!+\!1\) 到右端点前缀最大值单调不下降最小值单调不上升,所以可以直接用前缀和数组中的两个位置相减得到一段的贡献

\(j>k\) 的情况同理,预处理两个数组 \(S_{2},S’_{2}\)即可。

\(2\) 部分对左端点 \(i\) 的总贡献可表示为: \(ans_{2}[i]=\begin{cases}
mx*((S_{1}[k]\!-\!S_{1}[w1])+(mid\!-\!i\!+\!1)*(S’_{1}[k]\!-\!S’_{1}[w1]))&(j<k)\\
mi*((S_{2}[j]\!-\!S_{2}[w1])+(mid\!-\!i\!+\!1)*(S’_{2}[j]\!-\!S’_{2}[w1]))&(k<j)
\end{cases}\) 。

\((3).\) 完全不满足 \(mi,mx\) 的部分: \([w_{2}\!+\!1,R]\)

其实只要 \((2)\) 搞了出来,\((3)\) 就没啥难度了,一样的处理方法,维护两个前缀和数组 \(S_{3},S’_{3}\)。

\(3\) 部分对左端点 \(i\) 的总贡献可表示为: \(ans_{3}[i]=(S_{3}[R]\!-\!S_{3}[w2])+(mid\!-\!i\!+\!1)*(S’_{3}[R]\!-\!S’_{3}[w2])\) 。

最后,注意取膜!!!开 \(long\) \(long\) 防止中间乘爆。

时间复杂度为: \(O(nlogn)\) 。

【Code】

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define LL long long
#define Re register LL
using namespace std;
const LL N=5e5+3,logN=19,P=1e9;
LL n,ans,a[N],S1[N],S2[N],S3[N],S1_[N],S2_[N],S3_[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void sakura(Re L,Re R){
if(L==R){(ans+=a[L]*a[L]%P)%=P;return;}//这个特判必须要加
if(L+1==R){(ans+=(a[L]*a[L]%P+a[R]*a[R]%P+a[L]*a[R]%P*2%P)%P)%=P;return;}//这里好像不用特判也可以QAQ
Re mid=L+R>>1,mi=a[mid],mx=a[mid],i=mid,j=mid,k=mid;//注意j,k预处理为mid而不是mid+1
Re MI=a[mid+1],MX=a[mid+1];//这里MI,MX和上面的mi,mx取inf,-inf也可以
S1[mid]=S2[mid]=S3[mid]=S1_[mid]=S2_[mid]=S3_[mid]=0;//重置前缀和
for(Re i=mid+1;i<=R;++i){
MI=min(MI,a[i]),MX=max(MX,a[i]);//更新前缀最大值
(S1[i]=S1[i-1]+MI*(i-(mid+1)+1)%P)%=P,(S1_[i]=S1_[i-1]+MI)%=P;//递推更新S1
(S2[i]=S2[i-1]+MX*(i-(mid+1)+1)%P)%=P,(S2_[i]=S2_[i-1]+MX)%=P;//递推更新S2
(S3[i]=S3[i-1]+MI*MX%P*(i-(mid+1)+1)%P)%=P,(S3_[i]=S3_[i-1]+MI*MX%P)%=P;//递推更新S3
}
while(i>=L){
mi=min(mi,a[i]),mx=max(mx,a[i]);
while(j<R&&a[j+1]>mi)++j;//移动MI指针j
while(k<R&&a[k+1]<mx)++k;//移动MX指针k
Re w1=min(j,k),w2=max(j,k);//获取三部分的两个分界点
if(w1>mid)(ans+=mi*mx%P*((mid+1-i+1+w1-i+1)*(w1-(mid+1)+1)/2%P)%P)%=P;//完全满足的部分
if(j>w1)//满足mi但不满足mx
(ans+=mi*((S2[j]-S2[w1]+P)%P+(mid-i+1)*(S2_[j]-S2_[w1]+P)%P)%P)%=P;
if(k>w1)//满足mx但不满足mi
(ans+=mx*((S1[k]-S1[w1]+P)%P+(mid-i+1)*(S1_[k]-S1_[w1]+P)%P)%P)%=P;
(ans+=((S3[R]-S3[w2]+P)%P+(mid-i+1)*(S3_[R]-S3_[w2]+P)%P)%P)%=P;//完全不满足的部分
--i;//移动左指针
}
sakura(L,mid),sakura(mid+1,R);//递归搞下面
}
int main(){
// freopen("norma.in","r",stdin);
// freopen("norma.out","w",stdout);
in(n);
for(Re i=1;i<=n;++i)in(a[i]);
sakura(1,n);
printf("%lld\n",ans%P);
// fclose(stdin);
// fclose(stdout);
return 0;
}

最新文章

  1. PL/SQL异常处理
  2. 微软Dynamics 使用葡萄城的Wijmo 5提供移动端用户界面选择
  3. DEV winform treelist设置背景图像
  4. linux 给文件夹权限
  5. Effective Java 24 Eliminate unchecked warnings
  6. 转载:LBP的初步理解
  7. 转载:scikit-learn学习之决策树算法
  8. HDU 1393 Weird Clock (英语,纪念题)
  9. 百度PHP实习一面面试题-算法-二维有序矩阵的查找
  10. hdu2531之BFS
  11. WCF入门教程系列六
  12. poj 3053 Fence Repair(优先队列)
  13. 求解决!!!SystemVerilog于ModelSim在编译和执行
  14. Windows系统顽固文件删除方法
  15. Scala中的override
  16. 密码学Hash函数
  17. svn path already exists的解决办法
  18. BZOJ_4765_普通计算姬_分块+dfs序+树状数组
  19. ATS 相关
  20. SAS-决策树模型

热门文章

  1. 点云3D 目标检测
  2. [反汇编]函数开始部分利用mov ebx,esp找到返回地址(_KTRAP_FRAME结构)
  3. jQuery Validate表单校验
  4. PDF怎么转换为CAD文件?这两种方法你的会
  5. SpringBoot(六) SpringBoot整合Swagger2(自动化生成接口文档)
  6. JavaWeb创建一个公共的servlet
  7. Linux中的文件和目录结构详解
  8. PyQt5-TableWidget 表格视图
  9. C学习笔记(1)---数据类型,变量,储存类
  10. nginx代理ambassador,再转到mlfow-tracking服务