【题解】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;
}

【题解】Norma [COCI2014] [SP22343]的更多相关文章

  1. BZOJ3745 : [Coci2014]Norma

    考虑枚举右端点,用线段树维护[i,nowr]的答案. 当右端点向右延伸时,需要知道它前面第一个比它大/小的数的位置,这里面的最值将发生改变,这个使用单调队列求出,然后将所有的l都加1. 注意常数优化. ...

  2. 洛谷SP22343 NORMA2 - Norma(分治,前缀和)

    洛谷题目传送门 这题推式子恶心..... 考虑分治,每次统计跨过\(mid\)的所有区间的答案和.\(i\)从\(mid-1\)到\(l\)枚举,统计以\(i\)为左端点的所有区间. 我们先维护好\( ...

  3. BZOJ3745 / SP22343 NORMA2 - Norma 分治,CDQ分治

    要命的题目. 写法:分类讨论进行计算. 枚举过每一个\(mid\)的所有区间.对于左端点\(i∈[l, mid - 1]\),向左推并计算\([l,mid]\)范围内的最大\(/\)最小值. 然后右端 ...

  4. 【BZOJ3745】Norma(CDQ分治)

    [BZOJ3745]Norma(CDQ分治) 题面 BZOJ 洛谷 题解 这种问题直接做不好做,显然需要一定的优化.考虑\(CDQ\)分治. 现在唯一需要考虑的就是跨越当前中间节点的所有区间如何计算答 ...

  5. 【BZOJ3745】[Coci2015]Norma cdq分治

    [BZOJ3745][Coci2015]Norma Description Input 第1行,一个整数N: 第2~n+1行,每行一个整数表示序列a. Output 输出答案对10^9取模后的结果. ...

  6. COCI2014/2015 Contest#1 D MAFIJA【基环树最大独立点集】

    T1725 天黑请闭眼 Online Judge:COCI2014/2015 Contest#1 D MAFIJA(原题) Label:基环树,断环+树形Dp,贪心+拓扑 题目描述 最近天黑请闭眼在 ...

  7. SP22343 Norma--序列分治

    Norma 传送门 题意简化: 定义一个区间的贡献为 \(max*min*len\),求给定序列中所有子区间的总贡献和 题解 考虑 \(O(n*log_2n)\) 的复杂度的做法 数据结构??? yz ...

  8. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  9. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

随机推荐

  1. 故事 1:.net程序员成长经历

    我呢,是一名.NET程序员,在学校学的.NET和Java,在学校(校企合作)学了一年半的.NET方向的技术,后来觉得java也挺好的,又跑去学习Java,虽然学的很少,但是还是很希望能学好Java,所 ...

  2. java基础(14):Eclipse、面向对象、自定义数据类型的使用

    1. Eclipse的应用 1. 常用快捷操作 Ctrl+T:查看所选中类的继承树 例如,在下面代码中,选中Teacher类名,然后按Ctrl+T,就会显示出Teacher类的继承关系 //员工 ab ...

  3. Ajax的快速入门

    1.什么是ajax ajax是技术名词的缩写: Asynchronous:异步: JavaScript:JavaScript语言: And:和.与: XML:数据传输格式 ajax是客户端通过HTTP ...

  4. curl_multi_*模拟多线程异步用法

    测试环境: PHP版本:php7.0.10 mysql版本:5.7.14 测试用例:循环插入两千行数据到数据库 public function test_syn($pc){ // $pc = trim ...

  5. Java并发编程杂记(2)

    对象共享 synchronized 设定原子性确定临界区 + 内存可见性 要解决如下问题 防止一个线程在使用对象状态而另一个线程在修改对象状态:且当一个线程修改了对象状态后,对其他线程可见.   可见 ...

  6. 再来五道剑指offer题目

    再来五道剑指offer题目 6.旋转数组的最小数字 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4, ...

  7. Java 线程与多线程

    Java是一门支持多线程的编程语言! 什么是进程? 计算机中内存.处理器.IO等资源操作都要为进程进行服务. 一个进程上可以创建多个线程,线程比进程更快的处理单元,而且所占用的资源也小,多线程的应用也 ...

  8. [20190918]shrink space与ORA-08102错误.txt

    [20190918]shrink space与ORA-08102错误.txt 1.环境:SCOTT@test01p> @ ver1PORT_STRING                    V ...

  9. 计算机基础 python安装时的常见致命错误 pycharm 思维导图

    计算机基础 1.组成 人 功能 主板:骨架 设备扩展 cpu:大脑 计算 逻辑处理 硬盘: 永久储存 电源:心脏 内存: 临时储存,断电无 操作系统(windonws mac linux): 软件,应 ...

  10. 跳跃空间(链表)排序 选择排序(selection sort),插入排序(insertion sort)

    跳跃空间(链表)排序 选择排序(selection sort),插入排序(insertion sort) 选择排序(selection sort) 算法原理:有一筐苹果,先挑出最大的一个放在最后,然后 ...