2017多校联合第二场 1003题 hdu 6047 Maximum Sequence O(n) (有理有据地)贪心

34 篇文章 0 订阅
3 篇文章 0 订阅

题目链接


题意:

an 和 bn 是两个长度为 n 的序列,其中保证 1 <= bi <= n,

现要求根据 bn 来补全 an+1 ~ a2n 的数字,要求这一段和最大,

补全规则如下:

补 ai 时,从 bn 中不重复地挑选一个数字 bk, 要求 ai <= max { aj - j | bk <= j < i },


思路:

我们看补全规则,就是在说从 ai 中下标为 bk 的地方起一直向后到现有的最后一个位置中,找一个 aj - j 的最大值

因为 ai <= aj - j, 

所以 ai - i < aj - j,

所以对于 an+1 ~ a2n 而言,这一段的 aj - j 值是 递减 


显然,我们需要维护一个 maxa[1..n](因为保证了1 <= bi <= n),

maxa[i] 意为从第 i 个位置起一直到当前位置的 aj - j 的最大值,

又因为 an+1 ~ a2n 这一段的该值是递减的,所以随着 i 的位置向后移,这个数组的值事实上是压根 不需要更新 

但有一点要 注意 

就是 i = n + 1 时,是需要用 an+1 - (n+1) 去更新 maxa 的,因为这一段是开始递减的第一个值,它的值是可能比前面记录的值大的

当 i > n + 1 时,就不需要更新了


那么还剩下最后一个问题,该以什么顺序取 bk 呢?

其实我觉得挺显然的,直接 按 bk 的值从小到大就可行,

其实吧,只需要保证 an+1 - (n+1) 的值是 可以取到的最大值 即可,毕竟之后都不会再更新了

而按 bk 的值从小到大取,即可以保证每次在 a 中能取的范围逐渐缩小,取 an+1 时范围最大,很显然可以满足上述要求


时间复杂度O(n)


AC代码如下:

#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxn 250010
typedef long long LL;
const LL mod = 1e9 + 7;
using namespace std;
int a[maxn], b[maxn], maxa[maxn], diff[maxn], n;
inline int max(int a, int b) { return a > b ? a : b; }
void work() {
    for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); diff[i] = a[i] - i; }
    for (int i = 1; i <= n; ++i) scanf("%d", &b[i]);
    maxa[n] = diff[n];
    for (int i = n - 1; i > 0; --i) maxa[i] = max(maxa[i + 1], diff[i]);
    sort(b + 1, b + 1 + n);
    int x = maxa[b[1]];
    LL ans = x;
    for (int i = 1; i <= n; ++i) maxa[i] = max(maxa[i], x - (n + 1));
    for (int i = 2; i <= n; ++i) {
        x = maxa[b[i]];
        ans += (LL)x;
        ans %= mod;
    }
    printf("%lld\n", ans);
}
int main() {
    while (scanf("%d", &n) != EOF) work();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值