HDU6058 Kanade's sum
来源:互联网 发布:哪个淘宝客插件最牛 编辑:程序博客网 时间:2024/06/09 21:20
Kanade’s sum
题目大意就是给你一个1~n的一个排列,求满足题意的所有区间[l,r](区间长度至少为k且1 <= l <= r <= n)中第k大的数之和。
举个例子:
比如说给你 n = 4, k = 2,其中数列是a[4] = {3,1,4,2}。因为k = 2, 所以区间长度至少为2.
那么满足提议的区间有下列这些:
[1,2]:{3,1},第2(k = 2)大的数是1
[1,3]:{3,1,4},第2(k = 2)大的数是3
[1,4]:{3,1,4,2},第2(k = 2)大的数是3
[2,3]:{1,4},第2(k = 2)大的数是1
[2,4]:{1,4,2},第2(k = 2)大的数是2
[3,4]:{4,2},第2(k = 2)大的数是2
sum = 1 + 3 + 3 + 1 + 2 + 2 = 12;
这道题的做法我知道的有两种:
第一种,用一个for循环遍历a[],对于每一个a[i],去找它前面第k大和它后面第k大的位置(为什么是第k大而不是第k-1大呢?因为在第k大和第k-1大之间的数也是可以取的,要算上。先往下看),通过这些位置来计算出以a[i]为第k大的数的区间个数有几个,则sum += a[i] * cnt即可
在这个for循环里用两个数组left[]和right[]维护这些位置。left[1]表示a[i]左边比a[i]大的最近数的位置,以此类推。
上述例子中对于a[2] = 1,left[1] = 1, right[1] = 3。记left[]的长度为lcnt,right[]的长度为rcnt
对于cnt的计算类似于乘法原理,比如我们要算a[i]为第k大的区间个数,就从1到lcnt遍历left[]数组,当我们以left[s]为a[i]左边第s大的位置为最远端(近似)时,右边就只能以right[k - 1 - s]为最远端(近似),因为在我们要算的这个区间里比a[i]大的个数是k - 1个。那么就这[left[s],right[k - 1 - s]]一个区间满足吗?当然不是,正确答案是[l,r],其中left[s + 1]< l <= left[s],right[k - 1 - s] <= r < right[k - s]。那么根据乘法原理,左边能取left[s] - left[s + 1]个,右边能取right[k -s] - right[k - 1 -s]个,则cnt += (left[s] - left[s + 1])* (right[k -s] - right[k - 1 -s])。
这是正常情况,还有些特殊情况需要处理。这个就在程序里自己特判了。
最后说下时间复杂度,这种方法其实不是很好,因为是o(n²)的,但是由于题目要求2s,n²勉强能过,大概跑了1.7s左右。
第二种:先用pos[a[i]]=i记录下每个a[i]的位置,因为是1~n的排列,也就是说我们如果从1~n遍历for循环,每次计算i为第k大的区间个数,用数组模拟链表维护比i大的数,然后删除那个结点。复杂度o(nk),0.46s。
两种代码分别如下:
#include<cstdio>#include<cstring>using namespace std;typedef long long ll;const int maxn = 5e5 + 5;int a[maxn];int left[maxn];int right[maxn];int main(){ int cas; scanf("%d", &cas); while(cas--) { int n, k; scanf("%d%d", &n, &k); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); ll sum = 0; for(int i = 1; i <= n; i++) { int lcnt = 0, rcnt = 0, j = 0; for(j = i - 1; j >= 1; j--) { if(a[j] > a[i]) { lcnt ++; left[lcnt] = i - j; } if(lcnt >= k) break; } if(j <= 1) left[++lcnt] = i; for(j = i + 1; j <= n; j++) { if(a[j] > a[i]) { rcnt ++; right[rcnt] = j - i; } if(rcnt >= k) break; } if(j >= n) right[++rcnt] = n - i + 1; for(j = 1; j <= lcnt; j++) { if(k - j >= rcnt) continue; sum += (ll) a[i] * (left[j] - left[j - 1]) * (right[k - j + 1] - right[k - j]); } } printf("%lld\n", sum); }}
#include<cstdio>using namespace std;typedef long long ll;const int maxn = 5e5 + 10;int a[maxn], pos[maxn], pre[maxn], nxt[maxn];ll left[100], right[100];int n,k;void del(int x){ pre[nxt[x]] = pre[x]; nxt[pre[x]] = nxt[x];}ll cal(int x){ int lcnt = 0, rcnt = 0; for(int i = x; i > 0; i = pre[i]) { lcnt ++; left[lcnt] = i - pre[i]; if(lcnt == k) break; } for(int i = x; i <= n; i = nxt[i]) { rcnt ++; right[rcnt] = nxt[i] - i; if(rcnt == k) break; } ll res = 0; for(int i = 1; i <= lcnt; i ++) { if(k - i + 1 <= rcnt) { res += left[i] * right[k - i + 1]; } } return res;}int main(){ int lcnts = 0; int t; scanf("%d", &t); while(t--) { scanf("%d%d", &n, &k); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); pos[a[i]] = i; pre[i] = i - 1; nxt[i] = i + 1; } pre[0] = 0; nxt[n + 1] = n + 1; ll sum = 0; for(int i = 1; i <= n; i++) { int x = pos[i]; sum += cal(x) * i; del(x); } printf("%I64d\n", sum); }}
- HDU6058-Kanade's sum
- hdu6058-Kanade's sum
- hdu6058 Kanade's sum
- HDU6058-Kanade's sum
- HDU6058 Kanade's sum
- HDU6058-Kanade's sum
- HDU6058 Kanade's sum
- hdu6058 Kanade's sum
- HDU6058 Kanade's sum【模拟】
- 【HDU6058】Kanade's sum(暴力 or 链表)
- 【2017多校】HDU6058 Kanade's sum 【链表】
- HDU6058-Kanade's sum 链表+思维
- HDU6058 Kanade's sum(链表)
- HDU6058 2017杭电多校联赛第三场-Kanade's sum
- 2017杭电多校联赛team3 Kanade's sum hdu6058 快速幂
- Hdu6058 Kanade's sum(2017多校第3场)
- 2017多校训练Contest3: 1003 Kanade's sum hdu6058
- 2017杭电多校第三场 1003 Kanade's sum(hdu6058 区间内第k大)
- ionic2+cordova 遇到的坑
- Activity之间传递图片(Drawable,Bitmap)
- ubuntu中CTS环境搭建
- 51Nod-1747-近似多项式
- poj2139(最短路)
- HDU6058 Kanade's sum
- IaaS、PaaS和SaaS
- TextView 判断自动换行
- Spring 中的注解
- (一)Android数据结构学习之链表
- 车辆分类
- Java进阶资源汇总
- 如何在vue中引入第三方jquery,swiper等库(二)
- Power Designer 模块检查 错误总结