博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【51nod1592】数列积
阅读量:7022 次
发布时间:2019-06-28

本文共 2652 字,大约阅读时间需要 8 分钟。

Description

小明有一个含有n个数的数列 \(a_1,a_2,\cdots,a_n\)

他定义一个数列的积为\(\sum_{i=1}^n\sum_{j=i}^n|a_i-a_j|*(j-i)\)
他发现算出数列积实际上非常简单。因此他现在有了一个绝妙的主意。
他有Q个询问。
对于每个询问会给定两个参数\(l,r\)
他想知道的是,将\(a_l,a_{l+1},\cdots,a_r\)拿出来成为一个数列,问该数列的数列积是多少。
由于答案很大,他表示只要求出数列积对 \(2^{64}\) 取模后的值就可以了。

Solution

没有修改,考虑区间扩大缩小答案的增减量。

先讲讲0分做法,我们发现式子贡献可以拆开,维护四个值:个数,\(i\)的和,\(a_i\)的和,\(a_i\cdot i\)的和。于是有经典莫队算法,时间复杂度可以达到\(O(n\sqrt n log_2n)\)级别。此时如果运气好就可以过了。
将一个理论更优的做法:把序列分块,然后每一块的贡献可以暴力算。
考虑块与块之间的贡献,我们对于每一块维护它的排序块,枚举后面的块的排序块,用指针扫一下前面的排序块,算一下贡献即可。
然后就可以算第\(l\)块到第\(r\)块的贡献。
对于一个询问,它包含中间的完整块部分,还有左右的多余部分。
考虑多余部分怎么计算,先计算左(右)边自己的贡献:可以设一个\(f_{i,j}\)表示从\(i\)\(i+j\)的贡献,可以预处理;然后计算数对分别在左右多余部分的贡献:把多余部分标记,然后枚举右边多余部分所在的排序块,左边指针扫,遇到标记就加入即可。
还有左右多余元素与中间块的贡献:把所有元素排序,枚举每个块,然后计算一个元素与一个块的贡献,计算方法类似前面,但要判断一下元素与块的前后关系。
有了一个元素与一个块的贡献,就可以计算一个元素与前缀块贡献,于是可以计算一个元素与一段连续块的贡献。
结合上面的其他预处理,可以获得10分。
其实上面的预处理需要开很多\(n\sqrt n\)的数组,合理寻址优化是通过这题的关键。
结合上面的算法,或许可以获得满分。

Code

#include
#include
#include
#include
#define fo(i,j,k) for(int i=j;i<=k;++i)#define fd(i,j,k) for(int i=j;i>=k;--i)using namespace std;typedef unsigned long long ll;const int N=5e4+10,_N=250;int be[N];struct node{ int l,r;}c[_N];ll a[N];struct node2{ ll x; int p;}b[N],d[N];ll F[_N][_N],vl[_N][_N];ll f[_N][N],pre[_N][N];ll sum[N][_N];int bz[N];int read(){ char ch=' ';int t=0; for(;ch<'0' || ch>'9';ch=getchar()); for(;ch>='0' && ch<='9';ch=getchar()) t=(t<<1)+(t<<3)+ch-48; return t;}ll readll(){ char ch=' ';ll t=0; for(;ch<'0' || ch>'9';ch=getchar()); for(;ch>='0' && ch<='9';ch=getchar()) t=(t<<1)+(t<<3)+ch-48; return t;}bool cmp(node2 x,node2 y){ return x.x
c[j].l && b[p-1].x>a[u]) p--,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x; vl[j][i]+=a[u]*s1+s2*u-s3-a[u]*u*(ll)(c[j].r-p+1); } } } fo(l,2,tt) fo(i,1,tt-l+1){ int j=i+l-1; F[i][j]=F[i][j-1]+F[i+1][j]-F[i+1][j-1]+vl[i][j]; } fo(i,1,n-1) f[1][i]=(ll)abs((int)a[i]-(int)a[i+1]); fo(j,2,_n) fo(i,1,n-j) f[j][i]=f[j-1][i]+f[j-1][i+1]-f[j-2][i+1]+(ll)abs((int)a[i]-(int)a[i+j])*j; sort(d+1,d+n+1,cmp); fo(i,1,tt){ int p=c[i].l-1; ll s1=0,s2=0,s3=0; fo(j,1,n){ int k=d[j].p; if(be[k]==i) continue; while(p
c[i].l && b[p-1].x>a[k]) p--,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x; pre[i][j]+=(a[k]*s1+k*s2-s3-a[k]*k*(c[i].r-p+1))*(p
l1 && b[p-1].x>a[k]) if(bz[b[--p].p]==z) t++,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x; ans+=a[k]*s1+k*s2-s3-a[k]*k*t; } printf("%llu\n",ans); }}

转载于:https://www.cnblogs.com/sadstone/p/9130577.html

你可能感兴趣的文章
WPF中的DoubleAnimation
查看>>
JAVA学习笔记1
查看>>
2013-1-17 打开/关闭默认共享的命令
查看>>
oracle数据库中函数的递归调用
查看>>
pkgmgmt: Comparison between different Linux Systems..
查看>>
python--gevent协程及协程概念
查看>>
Java 打包成exe安装包
查看>>
EF执行出错~NotSupportedException
查看>>
A.出题人的RP值
查看>>
jQuery中$().each与$.each的区别
查看>>
大数据开发从入门小白到删库跑路(一)- 获取Hadoop
查看>>
ES6新特性概览
查看>>
[转] React Hot Loader 3 beta 升级指南
查看>>
slice,substr和substring的区别
查看>>
迭代器、生成器、面向过程编程
查看>>
使用async实现异步控制
查看>>
第一次实训作业
查看>>
Hash
查看>>
nginx 配置手机端PC端访问不同的项目
查看>>
SpriteKit-(SKNode)
查看>>