holiday题解

题目描述如下:

经过几个月辛勤的工作,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 = 200005;
struct seg
{
    int v;
}t[M<<2];
ll n,p,q,a[M],maxn;
bool flag;
ll read()
{
    ll ans = 0,op = 1;
    char ch = getchar();
    while(ch < ‘0‘ || ch > ‘9‘)
    {
        if(ch == ‘-‘) op = -1;
        ch = getchar();
    }
    while(ch >= ‘0‘ && ch <= ‘9‘)
    {
        ans *= 10;
        ans += ch - ‘0‘;
        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) >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    t[p].v = max(t[p<<1].v,t[p<<1|1].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) >> 1;
    if(kr <= mid) return query(p<<1,l,mid,kl,kr);
    else if(kl > mid) return query(p<<1|1,mid+1,r,kl,kr);
    else return max(query(p<<1,l,mid,kl,mid),query(p<<1|1,mid+1,r,mid+1,kr));
}

int main()
{
//    freopen("holiday.in","r",stdin);
//    freopen("holiday.out","w",stdout);
    n = read(),p = read(),q = read();
    rep(i,1,n) a[i] = read(),a[i] += a[i-1];
    build(1,1,n);
    rep(i,1,n)
    {
        ll dl = i*1ll+p-1,dr = i*1ll+q-1;
        if(dl > n) break;
        if(dr > n) dr = n;
        ll k = query(1,1,n,dl,dr) - a[i-1];
        if(!flag) maxn = k,flag = 1;
        else maxn = max(maxn,k);
    }
    printf("%lld\n",maxn);
    return 0;
}
/*
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 = 100005;
ll a, n, p, Q, head = 1, tail, q[M], f[M], sum[M], ans = -0X3f3f3f3f;
int main()
{
    scanf("%lld %lld %lld", &n, &p, &Q);
    rep(i,1,n)    scanf("%lld", &a),sum[i] = a + sum[i-1];
    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 0;
}

原文地址:https://www.cnblogs.com/captain1/p/9058537.html

时间: 2024-10-18 11:49:17

holiday题解的相关文章

ZOJ 3876 May Day Holiday 蔡勒公式

                                               H - May Day Holiday Description As a university advocating self-learning and work-rest balance, Marjar University has so many days of rest, including holidays and weekends. Each weekend, which consists o

hdoj 1827 Summer Holiday【强连通分量&amp;&amp;缩点】

Summer Holiday Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2256    Accepted Submission(s): 1050 Problem Description To see a World in a Grain of Sand And a Heaven in a Wild Flower, Hold Inf

Summer Holiday(强联通入度最小点)

Summer Holiday Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2319    Accepted Submission(s): 1082 Problem Description To see a World in a Grain of Sand And a Heaven in a Wild Flower, Hold Inf

hdu 4118 Holiday&#39;s Accommodation 树形dp

Holiday's Accommodation Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4118 Description Nowadays, people have many ways to save money on accommodation when they are on vacation.One of these ways is exchanging

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种

8.8联考题解

今天的T1让我怀疑我是不是在做奥赛题--这考的是什么知识点啊这个,会不会用绝对值函数? Evensgn 的债务 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Evensgn 有一群好朋友,他们经常互相借钱.假如说有三个好朋友A,B,C.A 欠 B 20 元,B 欠 C 20 元,总债务规模为 20+20=40 元.Evensgn 是个追求简约的人,他觉得这样的债务太繁杂了.他认为,上面的债务可以完全等价为 A 欠C20 元,B 既不欠别人,别人也不欠他.这样总债务规模就压缩到了 

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

(leetcode题解)Pascal&#39;s Triangle

Pascal's Triangle  Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Return [ [1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1] ] 题意实现一个杨辉三角. 这道题只要注意了边界条件应该很好实现出来,C++实现如下 vector<vector<int>> generate(int

2017ZZUACM省赛选拔试题部分题解----谨以纪念我这卡线滚粗的美好经历

写在前面: 其实心里有些小小的不爽又有点小小的舒畅,为啥捏?不爽当然是因为没被选拔上啦,舒畅捏则是因为没被选拔上反而让自己警醒,学长也提点很多很多."沉下去,然后一战成名"学长如是对我说,我很开心.其实这完全算不算是题解,只是我个人的一些小想法而已.而且到现在还有一题不会...让自己长点记性吧. 题目 A :聪明的田鼠 Time Limit: 1 Sec Memory Limit: 128 MB Description 田鼠MIUMIU来到了一片农田,农田可以看成是一个M*N个方格的矩