2017多校联合第三场 1003题 hdu 6058 Kanade's sum 链表

34 篇文章 0 订阅
1 篇文章 0 订阅

题目链接


题意:

给定数组 a,求其所有区间中第 k 大数的和。


思路:

考虑第 i 个位置上的数,如果它是某个区间内的第 k 大数,那么其左边有 i 个比它大的,右边有 k - 1 - i 个比它大的。(其实还是考虑个体对整体的贡献)


比赛的时候没敢直接写,怕会 T,也的确看到很多很多 T 的,但是之后看到有解题报告是直接模拟的并且也没有特殊的地方...0 0 不管怎样过了就要膜一发(http://blog.csdn.net/baidu_36227831/article/details/76555035

然后窝就不会了............


后来看了题解的链表,觉得用得真是太厉害了。

两个链表,其中一个是 每个元素指向其 前一个元素,另一个是 每个元素指向其 后一个元素。

从小到大枚举元素,那么它沿着链表向前跳 k 次与向后跳 k 次即为前面比它大的 k 个元素与后面比它大的 k 个元素。

每次处理完当前元素,就把它从链表里删除,这样保证了每次处理的元素都始终为链表里的最小元素,其向前找的与向后找的都是比它大的元素。


后来又看了别的解题报告,也有用 set 和 lower_bound 的,倒过来,从大到小将元素的下标插入 set,这样就同样也保证了当前处理的元素为 set 里的最小元素,每次 lower_bound 一下,就知道了左边和右边的分界线了,再往左往右找就可以了。


为啥当时一种方法都想不出来呢(暴风哭泣


AC代码如下(链表):

#include <bits/stdc++.h>
#define maxn 500010
typedef long long LL;
int pos[maxn], s1[110], s2[110], pre[maxn], suc[maxn];
void work() {
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; ++i) {
        int x;
        scanf("%d", &x);
        pos[x] = i;
    }
    for (int i = 1; i <= n; ++i) {
        pre[i] = i - 1; suc[i] = i + 1;
    }
    LL ans = 0;
    for (int i = 1; i <= n; ++i) {
        int p = pos[i], t1 = 0, t2 = 0;
        while (p > 0 && t1 < k + 1) {
            s1[t1++] = p;
            p = pre[p];
        }
        if (t1 <= k) s1[t1++] = 0;
        p = pos[i];
        while (p < n + 1 && t2 < k + 1) {
            s2[t2++] = p;
            p = suc[p];
        }
        if (t2 <= k) s2[t2++] = n + 1;

        for (int l = 0; l < t1 - 1; ++l) {
            int r = k - 1 - l;
            if (r >= t2 - 1) continue;
            ans += (LL)(s1[l] - s1[l + 1]) * (s2[r + 1] - s2[r]) * i;
        }

        p = pos[i];
        int prev = pre[p], succ = suc[p];
        pre[succ] = prev; suc[prev] = succ;
//        printf("%lld\n", ans);
    }
    printf("%lld\n", ans);
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) work();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值