【题解】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. 致Python初学者的六点建议

    Python是最容易学习的编程语言之一,其语法近似英语.通常,初学者只会遇到一些小麻烦,如强制缩进.在函数中使用self等. 然而,当开始阅读.复制和编辑他人代码时,麻烦就接踵而至了. 这里,我将解释 ...

  2. django中're_path'的用法

    1.re_path和path的作用是一样的.只不过're_path'是在写url的时候可以用正则表达式,功能更加强大.2.写正则表达式都推荐使用原生字符串.也就是以‘r’开头的字符串:r'move/' ...

  3. springmvc字符编码过滤器CharacterEncodingFilter浅析

      一.在web.xml中的配置 <!-- characterEncodingFilter字符编码过滤器 --> <filter> <filter-name>cha ...

  4. collection(list,set,map)集合详解

    一:java集合的体系结构如下: Java集合大致分为Set.List.Queue.Map四个体系 .Collection: List和Set,Queue继承自Collection接口. |--Lis ...

  5. PHP接口自动化测试框架实现

    在上一份工作中,我有一部分工作是在维护一套接口自动化测试,这一篇文章,我来介绍这套接口自动化框架的设计思路. 我们来看一个简单的PHP实现的超简单的接口. ... //报名验证 private fun ...

  6. python 中文分词库 jieba库

    jieba库概述: jieba是优秀的中文分词第三方库 中文文本需要通过分词获得单个的词语 jieba是优秀的中文分词第三方库,需要额外安装 jieba库分为精确模式.全模式.搜索引擎模式 原理 1. ...

  7. Linux—网络通讯管理命令

    一.ping命令 . ping 主机名 . ping 域名 [root@localhost ~]# ping www.baidu.com . ping IP地址 [root@localhost ~]# ...

  8. 渗透测试学习 十九、 XSS跨站脚本漏洞详解 续2

    二阶注入环境搭建 74cms 3.4 直接将源码放在PHPstudy的www路径下,在地址栏中输入127.0.0.1回车 然后进入网站首页,在填写简历里面存在二阶注入 先注册一个账号 创建简历 前面的 ...

  9. eslint代码规范检测

    1.如果在 vue init webpack projectname 时选择了eslint(Yes),则   npm uninstall eslint 2.在webpack.base.conf.js里 ...

  10. git 推送本地项目到远程库

    git 推送本地项目到远程库 1@DESKTOP-3H9092J MINGW64 /e/mozq/00store/01/SmartCard_MS $ git init Initialized empt ...