题目链接:1037F - Maximum Reduction

题目大意:给出一段代码,给你一个长度为n的数组和数字k,求程序运行结果,mod 1e9+7输出

     简单翻译下代码的意思,初始定义一个空数组b,分别查询区间[1,k];[2,k+1];...;[n-k+1,n]的最大值,并将这 n-k+1 个区间最大值放入b,将b中元素之和加入到ans里,并把这个长度为n-k+1的数组b放入到下一层的递归,这样就要再求n-2k+1次、n-3k+1次最大值,直到数组的长度小于k(即无法求区间长度为k的最大值)

题解:我一开始也不知道从何入手,于是就写了下当n=10,k=3的时候要求的是哪些区间最大值的和,写出来是这样

   [1,3][2,4][3,5][4,6][5,7][6,8][7,9][8,10]

   [1,5][2,6][3,7][4,8][5,9][6,10]

   [1,7][2,8][3,9][4,10]

   [1,9][2,10]

   可以发现当左端点 l 固定的时候,右端点只可能是 l+t(k-1),称这样的区间是有用的区间

   然后我们再来考虑另外一件事情,对于上述的每一个区间,这些区间的最大值都是这些区间中的某一个数【废话←_←】

   于是我们就可以开始考虑每个数被当做区间最大值的次数是多少这个问题。对于任意一个位置x上的数,他都是有一个影响范围的,即在这个范围里,任意一个包含x的区间,a[x]都是这个区间内的最大值。因此我们要先预处理出每个数的左边最后一个比他小的数是多少,他右边最后一个比他小的数是多少,记为l[i],r[i],显然区间[ l[i],r[i] ]是a[i]这个数的影响范围。这时对任意的x,我们只需要求出在区间[ l[x],r[x] ]内,有多少个有用的区间包含x

   那怎么计算有用区间的个数呢?这就需要用到之前求出的性质了。显然一个区间[l,r]有用,当且仅当 (r-l)=t(k-1) 其中t为正整数。因此区间[l,r]中以l为左端点的有用的区间个数为 \(\left \lfloor \frac{r-l}{k-1} \right \rfloor\) ,故区间[l,r]中的有用区间数为 \(\sum_{i=l}^{r}\left \lfloor \frac{r-i}{k-1} \right \rfloor\) 。但注意到,这些区间里有一些是没有包含到x的,所以若设g(l,r,x)表示区间[l,r]中包含x的有用区间数,则$$g(l,r,x)=\sum_{i=l}^{x}\left \lfloor \frac{r-i}{k-1} \right \rfloor - \sum_{i=l}^{x-1}\left \lfloor \frac{x-1-i}{k-1} \right \rfloor$$

   这个式子表示的就是左端点在x及x左边的有用区间总数 减去 右端点小于x的有用区间总数。这样循环一遍的复杂度是O(2(x-l)),也就是说如果直接暴力求解的话是铁定会超时的,所以我们需要进一步优化

   

   upd:看到这里可以直接点开评论区第一条回复,用预处理前缀和优化即可

   这里优化的姿势有两种,一种是考虑这个式子可以转换为若干个等差数列之和,直接套公式就好,这种做法写起来简单但需要一定时间思考,当时做这道题的时候懒得细想就用了另一种方法

   第二种方法就是用类欧几里得算法做了,由于本人比较菜,对这方面的知识不太了解,也只会套模板,就不详细介绍了_(:з」∠)_这个相关的知识点百度一下都有的233333

   类欧几里得算法可以对 \(f(a,b,c,n)=\sum_{i=0}^{n}\left \lfloor \frac{ai+b}{c} \right \rfloor\)这样的函数进行复杂度为O(log n)的计算,可以发现\(g(l,r,x)=f(1,r-x,k-1,x-l)-f(1,0,k-1,x-l-1)\)。于是将之前求得的式子对着板子套进去就可以快速求解了~

#include<bits/stdc++.h>
using namespace std;
#define N 1000001
#define LL long long
#define MOD 1000000007
LL n,k,a[N],l[N],r[N],ans;
LL S(LL k){return (k*(k+)/2ll)%MOD;}
LL f(LL a,LL b,LL c,LL n)
{
if(!a)return ;
if(a>=c || b>=c)
return ((a/c)*S(n)%MOD+(n+)*(b/c)%MOD+f(a%c,b%c,c,n))%MOD;
LL m=(a*n+b)/c;
return (m*n%MOD-f(c,c-b-,a,m-)+MOD)%MOD;
}
LL get(LL l,LL r,LL x)
{
return (f(,r-x,k,x-l)-f(,,k,x-l-)+MOD)%MOD;
}
int main()
{
scanf("%I64d%I64d",&n,&k);
for(LL i=;i<=n;i++)
scanf("%I64d",&a[i]);
l[]=,r[n]=n;
for(LL i=;i<=n;i++)
{
LL _=i;
while(_> && a[i]>=a[_-])
_=l[_-];
l[i]=_;
}
for(LL i=n-;i>=;i--)
{
LL _=i;
while(_<n && a[i]>a[_+])
_=r[_+];
r[i]=_;
}
k--;
for(LL i=;i<=n;i++)
ans+=get(l[i],r[i],i)*a[i]%MOD,ans%=MOD;
printf("%I64d\n",ans);
return ;
}

   可以发现由于这里的套模板的a为1,所以最多递归两次就能得到答案,因此这部分的时间复杂度可以看成O(n)

   另外还有一个注意事项,就是在处理数组l,r的时候要半开半闭地处理,即往右边是找小于等于,往左边是找小于这样,否则当出现重复的数字的时候会重复计算答案

最新文章

  1. 利用Spring AOP机制拦截方法一例
  2. 【poj2409】 Let it Bead
  3. 虚方法的调用是怎么实现的(单继承VS多继承)
  4. pyinstall 使用笔记
  5. LINUX Shell 下求两个文件交集和差集的办法
  6. 检查Linux Bash安全漏洞以及各环境修复解决方法
  7. class 添加样式,删,开关 【选择】addClass,removeClass,toggleClass
  8. nodejs之querystring模块
  9. Microsoft .Net Remoting系列专题之三:Remoting事件处理全接触
  10. 虚拟机迁移(QEMU动态迁移,Libvirt动(静)态迁移)
  11. ARouter学习随笔
  12. SpringBoot 注解
  13. Javascript 运行上下文和作用域链
  14. Python 练习: 简单的用户登录判断
  15. Selenium的自我总结2_元素基本操作
  16. Git创建本地仓库并推送至远程仓库
  17. js中常见继承方式
  18. 杂项-分布式-EDAS:深度解析阿里云EDAS服务
  19. html5多媒体Video/Audio
  20. Spring Boot多数据源

热门文章

  1. java.lang.NumberFormatException: multiple points错误问题
  2. [Web 前端] mobx教程(三)-在React中使用Mobx
  3. iOS:解决UITextView自适应高度粘贴大量文字导致显示不全的问题
  4. android:Android中用文件初始化sqlite数据库(zz)
  5. Android Studio3.0.1集成Git
  6. shell编程学习笔记(八):Shell中if判断的使用
  7. shell编程学习笔记(七):Shell中将指定内容输出到文件中
  8. linux 通过nvm安装node
  9. VC 预定义宏
  10. Effective Java 第三版——75. 在详细信息中包含失败捕获信息