HDU - 3486 Interviewe RMQ优化

题目

nmnmnm 有 n 个 人 参 加 面 试 , 公 司 只 需 要 m 个 人 , 于 是 将 n 个 人 分 成 m 段 每 段 ⌊ n m ⌋ 人 , 且 从 每 段 中 选 出 能 力 最 大 的 人 。
mmk 现 在 要 求 计 算 出 一 个 最 小 的 m 满 足 选 出 的 m 人 的 能 力 和 严 格 大 于 k 。


题解

Step1 S t e p 1
网上很多使用二分的方法
其实是不对的,我们可以讨论一下二分为什么不对。
m,mk 思 路 就 是 二 分 m , 去 判 断 选 出 的 m 人 的 和 是 否 大 于 k
mm 大 于 说 明 当 前 m 是 满 足 的 , 于 是 更 改 上 界 , 再 去 判 断 是 否 存 在 更 小 的 m 也 满 足 。
m 否 则 就 更 改 下 界 , 去 寻 找 更 大 的 m 满 足 条 件 。
m使 这 里 默 认 了 一 个 条 件 即 m 越 大 获 得 能 力 和 越 大 。 这 也 是 能 使 用 二 分 的 单 调 性 。
乍一看没什么不对,选的人多了嘛,肯定能力和越大。
可是实际上并不满足这个性质,因为题目是从一段中选出一个最大的。
3,4,55,55,2,3 比 如 例 子 3 , 4 , 55 , 55 , 2 , 3
m=255+55=110;m=34+55+3=62 当 m = 2 时 , 答 案 为 55 + 55 = 110 ; m = 3 时 答 案 为 4 + 55 + 3 = 62
(m) 并 不 满 足 单 调 递 增 性 ( m 越 大 和 越 大 )
所 以 二 分 是 错 误 的 , 但 是 二 分 能 过 只 能 说 数 据 不 强 , 或 者 出 题 人 有 意 放 过 。

Step2 S t e p 2
那么接下来就来讨论正确的做法
,m 既 然 不 能 二 分 , 那 只 能 从 小 到 大 枚 举 m 了 吧
使STO(1)m 使 用 S T 表 查 询 时 间 是 O ( 1 ) 的 那 么 朴 素 的 枚 举 m 的 时 间 复 杂 度 为

O(n2) O ( n 2 )

但 是 这 里 存 在 一 个 优 化
nmn 我 们 知 道 ⌊ n m ⌋ 只 有 n 种 值 。
mnm 也 就 是 说 对 于 不 同 的 m , ⌊ n m ⌋ 是 一 样 的 , 即 每 一 段 的 人 数 是 一 样 的 。
nm 于 是 对 于 相 同 的 ⌊ n m ⌋ 我 们 没 必 要 从 头 开 始 , 只 需 要 继 续 累 计 和 即 可

n=7 例 子 n = 7
m=4,nm=1 假 设 我 们 已 经 枚 举 到 了 m = 4 , ⌊ n m ⌋ = 1
14 也 就 是 每 1 个 人 取 一 个 最 大 值 , 现 在 已 经 取 得 了 前 4 个 人 的 值 。
m=5,nm=11 当 m = 5 , , ⌊ n m ⌋ = 1 还 是 每 1 个 人 取 一 个 最 大 值 。
4,5 我 们 已 经 知 道 前 4 个 人 的 最 大 值 的 和 , 对 于 第 5 个 , 只 需 要 加 上 即 可 。
nm[m,nnm] 而 对 于 ⌊ n m ⌋ 出 现 的 范 围 是 [ m , n ⌊ n m ⌋ ]
时间复杂度为

O(nlog(n)) O ( n l o g ( n ) )


代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
typedef long long ll;
int arr[maxn],mm[maxn];
int n;ll k;
struct ST{
    int dp[maxn][20];
    void init() {
        mm[0] = -1;
        for(int i=0;i<n;i++) {
            dp[i][0] = arr[i];
            mm[i+1] = (((i+1) & i) == 0) ? mm[i] + 1 : mm[i];
        }
        for(int j=1;(1<<j)<=n;j++) {
            for(int i=0;i+(1<<j)-1<n;i++) {
                dp[i][j] = max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int query(int l,int r) {
        int k = mm[r-l+1];
        // while((1<<(k+1)) <= (r - l + 1)) k++;
        return max(dp[l][k],dp[r-(1<<k)+1][k]);
    }
}st;
int solve() {
    st.init();
    for(int m=2;m<=n;) {
        int len = n / m,c = 1,l;
        int r = n / len;
        ll res = 0;
        for(;m<=r;m++) {
            for(;c<=m;c++) {
                l = c * len;
                res += st.query(l-len,l-1);
                if(res > k) return m;
            }
        }
    }
    return -1;
}
int main()
{
    while(~scanf("%d%lld",&n,&k),n != -1 && k != -1) {
        ll sum = 0;
        int tmp = 0;
        for(int i=0;i<n;i++) {
            scanf("%d",&arr[i]);
            sum += arr[i];
            tmp = max(tmp,arr[i]);
        }
        if(sum <= k) {
            printf("-1\n");
            continue;
        }
        if(tmp > k) {
            printf("1\n");
            continue;
        }
        printf("%d\n",solve());
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值