【题解】Norma [COCI2014] [SP22343]
【题解】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]的更多相关文章
- BZOJ3745 : [Coci2014]Norma
考虑枚举右端点,用线段树维护[i,nowr]的答案. 当右端点向右延伸时,需要知道它前面第一个比它大/小的数的位置,这里面的最值将发生改变,这个使用单调队列求出,然后将所有的l都加1. 注意常数优化. ...
- 洛谷SP22343 NORMA2 - Norma(分治,前缀和)
洛谷题目传送门 这题推式子恶心..... 考虑分治,每次统计跨过\(mid\)的所有区间的答案和.\(i\)从\(mid-1\)到\(l\)枚举,统计以\(i\)为左端点的所有区间. 我们先维护好\( ...
- BZOJ3745 / SP22343 NORMA2 - Norma 分治,CDQ分治
要命的题目. 写法:分类讨论进行计算. 枚举过每一个\(mid\)的所有区间.对于左端点\(i∈[l, mid - 1]\),向左推并计算\([l,mid]\)范围内的最大\(/\)最小值. 然后右端 ...
- 【BZOJ3745】Norma(CDQ分治)
[BZOJ3745]Norma(CDQ分治) 题面 BZOJ 洛谷 题解 这种问题直接做不好做,显然需要一定的优化.考虑\(CDQ\)分治. 现在唯一需要考虑的就是跨越当前中间节点的所有区间如何计算答 ...
- 【BZOJ3745】[Coci2015]Norma cdq分治
[BZOJ3745][Coci2015]Norma Description Input 第1行,一个整数N: 第2~n+1行,每行一个整数表示序列a. Output 输出答案对10^9取模后的结果. ...
- COCI2014/2015 Contest#1 D MAFIJA【基环树最大独立点集】
T1725 天黑请闭眼 Online Judge:COCI2014/2015 Contest#1 D MAFIJA(原题) Label:基环树,断环+树形Dp,贪心+拓扑 题目描述 最近天黑请闭眼在 ...
- SP22343 Norma--序列分治
Norma 传送门 题意简化: 定义一个区间的贡献为 \(max*min*len\),求给定序列中所有子区间的总贡献和 题解 考虑 \(O(n*log_2n)\) 的复杂度的做法 数据结构??? yz ...
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
随机推荐
- 并发编程-epoll模型的探索与实践
前言 我们知道nginx的效率非常高,能处理上万级的并发,其之所以高效离不开epoll的支持, epoll是什么呢?,epoll是IO模型中的一种,属于多路复用IO模型; 到这里你应该想到了,sele ...
- 简单文件传输协议TFTP分析还原
- 协议介绍 TFTP有如下特征: 1.UDP承载,请求端口固定为69: 2.没有列出目录内容功能: 3.无验证和加密机制: 4.仅有读取或写入文件功能: 5.支持三种不同的传输模式:"ne ...
- Gradle在Android中的简单使用
Gradle在Android中简单的使用 还望支持个人博客站:http://www.enjoytoday.cn Android Studio 使用gradle进行工程构建,为了更好的了解整个andro ...
- 透过systemctl管理mysqld服务
1. 背景 CentOS 7.x 之前的版本,系统启动时,第一支呼叫的程序是 init ,然后 init 去唤起所有的系统所需要的服务,无论是本地服务还是网络服务.所有的服务启动脚本都放置于 /etc ...
- 2.idea安装JavaCC插件.md
eclipse中插件安装JavaCC插件请参考博客:https://blog.csdn.net/qq_29232943/article/details/62439283 接下来是在idea中安 ...
- Mysql Join-连接查询(中)
Mysql Join-连接查询(中) 认识 就我平时的数据接触来看, 连接查询也没有很复杂,不够是非常需要耐心和逻辑的, 一点点将数据查出来, 拼接等. 没有什么技巧, 多练习就会了. 无非就是表之间 ...
- [Go] 利用channel形成管道沟通循环内外
这个要解决的问题是,比如如果有一个大循环,取自一个大的文件,要进行逻辑处理,那么这个逻辑的代码要放在循环每一行的循环体里面,这样有可能会出现一个for循环的逻辑嵌套,一层又一层,类似俄罗斯套娃.如果放 ...
- BouncyCastle配置
1.BouncyCastle简介 BouncyCastle是一款开源的密码包,其中包含了大量的密码算法,使用BouncyCastle的目的就是为了扩充算法支持 下载地址最新加密组件包 http://w ...
- Vim基础配置
vim 个性化设置 安装插件管理器Vundle: 创建目录: mkdir -p ~/.vim/bundle 下载文件: git clone https://github.com/VundleVim/V ...
- toast文本提示信息元素获取
在做自动化的过程中,我们有可能会遇到toast提示语,这种提示语只会显示一两秒的样子,导致我们获取元素很困难 今天总结下怎么获取这个toast元素吧 例如下图所示的这种toast提示 图中红框的这种元 ...