题目描述如下:

经过几个月辛勤的工作,FJ 决定让奶牛放假。假期可以在 1…N 天内任意选择一段(需要连
续),每一天都有一个享受指数 W。但是奶牛的要求非常苛刻,假期不能短于 P 天,否则奶
牛不能得到足够的休息;假期也不能超过 Q 天,否则奶牛会玩的腻烦。FJ 想知道奶牛们能
获得的最大享受指数。
Input(holiday.in)
第一行:N,P,Q.
第二行:N 个数字,中间用一个空格隔开。
Output(holiday.out)
一个整数,奶牛们能获得的最大享受指数。
Sample Input
5 2 4
-9 -4 -3 8 -6
Sample Output
5
Limitation
time:1s
memory:65536kb
50% 1≤N≤10000
100% 1≤N≤100000
1<=p<=q<=n
Hint
选择第 3-4 天,享受指数为-3+8=5。

这道题当时看的时候大概想了5分钟左右吧……结合最近学的RMQ和线段树。因为我们要求一段连续区间和的最大值,我们可以想到首先必然要枚举这个假期开始的日期,之后你的起始点确定,终点必然在[i+p-1,i+q-1]这段区间之内。这样的话其实范围就相对确定了,对于一段区间的和,我们可以先手初始化前缀和之后以相减的方法求出,而最关键的位置就在于如何确定终点?因为我们要求最大值,而前面一段区间的前缀和已经被确定,所以只要在[i+p-1,i+q-1]中找出前缀和最大值即可。而每段区间的最大值完全可以用线段树或者st表维护。这样就可以解决这个问题了。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n') typedef long long ll;
using namespace std;
const int M = ;
struct seg
{
int v;
}t[M<<];
ll n,p,q,a[M],maxn;
bool flag;
ll read()
{
ll ans = ,op = ;
char ch = getchar();
while(ch < '' || ch > '')
{
if(ch == '-') op = -;
ch = getchar();
}
while(ch >= '' && ch <= '')
{
ans *= ;
ans += ch - '';
ch = getchar();
}
return ans * op;
} void build(ll p,ll l,ll r)
{
if(l == r)
{
t[p].v = a[l];
return;
}
ll mid = (l+r) >> ;
build(p<<,l,mid);
build(p<<|,mid+,r);
t[p].v = max(t[p<<].v,t[p<<|].v);
} ll query(int p,int l,int r,int kl,int kr)
{
if(l == kl && r == kr)
{
return t[p].v;
}
ll mid = (l+r) >> ;
if(kr <= mid) return query(p<<,l,mid,kl,kr);
else if(kl > mid) return query(p<<|,mid+,r,kl,kr);
else return max(query(p<<,l,mid,kl,mid),query(p<<|,mid+,r,mid+,kr));
} int main()
{
// freopen("holiday.in","r",stdin);
// freopen("holiday.out","w",stdout);
n = read(),p = read(),q = read();
rep(i,,n) a[i] = read(),a[i] += a[i-];
build(,,n);
rep(i,,n)
{
ll dl = i*1ll+p-,dr = i*1ll+q-;
if(dl > n) break;
if(dr > n) dr = n;
ll k = query(,,n,dl,dr) - a[i-];
if(!flag) maxn = k,flag = ;
else maxn = max(maxn,k);
}
printf("%lld\n",maxn);
return ;
}
/*
5 2 4
-9 -4 -3 8 -6
*/

这段代码的时间复杂度是O(nlogn),有没有更好的做法?

有!因为我们在上面的做法中是维护最大值然后减前缀和,那么我们同样可以用前缀和去减前面的最小值!即f[i] = sum[i] - min(sum[1…i])。那么我们可以用单调队列来维护一个单调递增的序列之后每次用当前的值去减队首元素(最小值)即可。

这样的话由于每个点都进入队列一次,所以一共出队列必然<=n次,时间复杂度是O(n),最多不过O(2*n).

看一下代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
typedef long long ll;
using namespace std;
const int M = ;
ll a, n, p, Q, head = , tail, q[M], f[M], sum[M], ans = -0X3f3f3f3f;
int main()
{
scanf("%lld %lld %lld", &n, &p, &Q);
rep(i,,n) scanf("%lld", &a),sum[i] = a + sum[i-];
rep(i,p,n)
{
while(sum[q[tail]] >= sum[i-p] && head <= tail) tail--;//因为i-p处的元素必须入队,所以队中一切比sum[i-p]大的元素必须出队
q[++tail] = i - p;//把sum[i-p]压入队列
while(q[head] < i - Q) head++;//因为我们最多只能取到i-q这么长的区间,所以在这个区间之前的所有元素必须出列。
f[i] = sum[i] - sum[q[head]];
ans = max(ans, f[i]);
}
printf("%lld\n", ans);
return ;
}

最新文章

  1. Linux Supervisor 守护进程基本配置
  2. python中的类中属性元素加self.和不加self.的区别
  3. HttpWebRequest get/post方法实现
  4. Android项目--tabhost
  5. AngularJS的过滤器$filter
  6. 系统学习DOM事件机制
  7. Unix中的I/O模型
  8. [Swift]LeetCode86. 分隔链表 | Partition List
  9. 使用JBolt新建Maven版工程步骤
  10. 【Tensorflow】Tensorflow入门教程
  11. nuxtjs中使用路由守卫
  12. 在 Linux 平台及 IPv4 环境中构建 IPv6局域网 测试环境
  13. android摄像头(camera)之 v4l2的c测试代码【转】
  14. SSM项目开发中的实体定义以及MySQL表格设计
  15. Oracle数据库导入报ORA-39083处理
  16. SQL Server全文搜索
  17. go的精选类库
  18. 查看firefox浏览器 驱动geckodriver.exe文件的版本号的方法,以及下载链接
  19. Go 开发
  20. centos 7 安装rabbitmq 3.6.12

热门文章

  1. Codeforces 655E Beautiful Subarrays【01trie树】
  2. Leetcode 数组问题2:买卖股票的最佳时机 II
  3. sql查询语句整理
  4. 分享一个ci 框架下取不到cookie的问题
  5. poj 2154 Color 欧拉函数优化的ploya计数
  6. 【转载】5种网络IO模型
  7. android笔记5——同一个Activity中Fragment的切换
  8. Here is the reason why Fengguang turns from ipmitool to freeipmi
  9. C#WinForm窗体监听/拦截操作动作
  10. Android图表AChartEngine