您的位置:首页 > 其它

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

2017-08-02 11:17 369 查看
题目链接

题意:

给定数组 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: