题目大意

给出 $1$ 到 $n$ 的一个排列($n\le 10^5$),记做 $a_1, a_2, \dots, a_n$ 。(注:原题面表述为:“给定 $n$ 个互不相同且不超过 $n$ 的整数”,并未指明 $a_i$ 是正数,属描述不确切,实际题意如此。见管理员赛后发的题解)求所有可能的区间中前 $k$ ($k\le \min(n,50)$)大的数之和的总和,对于长度不足 $k$ 的区间则全部累加。(注:前 $k$ 大是指“最大的 $k$ 个”)

解法

这是一道“标准的”区间统计类问题。不难想到思路:考虑 $a_i$ 在多少个区间内能成为前 $k$ 大的数之一。

进一步转化成:

对每个数 $a_i$,求它 前面/后面 离它最近且比它大的 $k$ 个数的下标。

这个问题把我难住了。看了管理员的题解后,学到正解如下。

按 $a_i$ 从小到大的顺序求解。原因在于:若 $a_j < a_i$ 则 $a_j$ 对 $a_i$ 的结果毫无影响,故而按此顺序求解,求出 $a_i$ 对应的答案过后便可将 $a_i$ 删除。不难想到,可以用“双向链表”来维护原序列;每次向前 $k$ 跳,再向后 $k$ 跳即可。复杂度 $O(nk)$ 。

我并未手写双向链表,而是用了 std::list。我对 std::list 的接口不熟悉,这次又到 cppreference.com 上温习了一下。

这道题用 std::list 要注意的点有:

  1. std::list<T>::iterator 属于 bidirectional iterator ,仅支持 ++-- 运算,不能加/减一个整数也不能两个 iterator 做差。
  2. 要注意区别 std::list<T>::iteratorstd::list<T>::reverse_iterator 这两个类型。二者貌似是可以互相做类型转换的。例如
std::list<list> a{1,2,3,4};
std::<list>::iterator it=a.begin();
std::<list>::reverse_iterator rit = a.rend();
std::<list>::reverse_iterator rit2(it);
assert(rit == rit2);

需要注意的是,当 iterator 转成 reverse_iterator 时,会向前移动一次,即返回的 reverse_iterator object 指向的是原 iterator object 所指向的前一位置。

另外,在声明 reverse_iterator 时若要做此类型转换则只能以 (){} 的方式赋初始值不能以 = 的方式(至少 g++ 如此)。

Implementation

#include <bits/stdc++.h>
using namespace std;
list<int> a;
const int N =1e5+5;
list<int>::iterator iter[N];
int pre[51];
int post[51];
int main(){
int n, k;
cin >> n >> k;
for(int i=1; i<=n; i++){
int x;
cin >> x;
// std::list<T>::iterator is a BidirectionalIterator and does not support arithmetic operation, it can only be incremented or decremented.
a.push_back(i);
auto tmp=a.end();
iter[x]=--tmp;
// printf("%d\n", *iter[x]);
}
long long ans =0;
for(int i=1; i<=n; i++){
int c1 = 1;
// auto tmp = iter[i];
// how to convert an std::list<T>::iterator into an std::list<T>::reverse_iterator?
// A: You can use std::make_reverse_iterator()
// Note: the resulting reverse iterator is the original iterator moved one step forward, so that std::make_reverse_iterator(a.begin()) returns a.rend() and std::make_reverse_iterator(a.end()) returns a.rbegin().
auto it = iter[i];
// list<int>::reverse_iterator riter(++it);
list<int>::reverse_iterator riter{++it};
// cout << "x: "<< *riter << '\n';
while(1){
++riter;
if(riter==a.rend()){
pre[c1]=0;
break;
}
// cout << *riter<<'\n';
pre[c1] = *riter;
if(c1==k) break;
++c1;
}
int c2=1;
auto tmp = iter[i];
while(1){
++tmp;
if(tmp==a.end()){
post[c2]=n+1;
break;
}
post[c2]=*tmp;
if(c2==k) break;
++c2;
}
assert(c2 <= k);
int pos = *iter[i];
// for(int i=1; i<=c1; i++)
// cout << pre[i] << ' ';
// cout << '\n'; // for(int i=1; i<=c2; i++)
// cout << post[i] << ' ';
// cout << '\n';
pre[0] = pos;
for(int j=1; j<=c1; j++){
// cout <<":: "<< i << ' ' << pre[j] << ' ' << post[min(c2,k+1-j)]<<'\n';
ans += (long long)(i)*(pre[j-1]-pre[j])*(post[min(c2,k+1-j)]-pos);
}
a.erase(iter[i]);
}
cout << ans << '\n';
return 0;
}

hihoCoder #1661 数组区间的更多相关文章

  1. hdu 4638 树状数组 区间内连续区间的个数(尽可能长)

    Group Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm ...

  2. 树状数组区间加法&区间求和操作

    树状数组区间加法&区间求和操作 一般的树状数组解决区间加&单点询问并不复杂 但是要解决区间求和... 我们假设原数组是\(\{a_i\}\),差分数组\(\{d_i=a_i-a_{i- ...

  3. hdu 1754 I Hate It(树状数组区间求最值)2007省赛集训队练习赛(6)_linle专场

    题意: 输入一行数字,查询第i个数到第j个数之间的最大值.可以修改其中的某个数的值. 输入: 包含多组输入数据. 每组输入首行两个整数n,m.表示共有n个数,m次操作. 接下来一行包含n个整数. 接下 ...

  4. hdu 1116 敌兵布阵(树状数组区间求和)

    题意: 给出一行数字,然后可以修改其中第i个数字,并且可以询问第i至第j个数字的和(i <= j). 输入: 首行输入一个t,表示共有t组数据. 接下来每行首行输入一个整数n,表示共有n个数字. ...

  5. 【bzoj5173】[Jsoi2014]矩形并 扫描线+二维树状数组区间修改区间查询

    题目描述 JYY有N个平面坐标系中的矩形.每一个矩形的底边都平行于X轴,侧边平行于Y轴.第i个矩形的左下角坐标为(Xi,Yi),底边长为Ai,侧边长为Bi.现在JYY打算从这N个矩形中,随机选出两个不 ...

  6. 【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

    题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数 ...

  7. 【bzoj3132】上帝造题的七分钟 二维树状数组区间修改区间查询

    题目描述 “第一分钟,X说,要有矩阵,于是便有了一个里面写满了0的n×m矩阵. 第二分钟,L说,要能修改,于是便有了将左上角为(a,b),右下角为(c,d)的一个矩形区域内的全部数字加上一个值的操作. ...

  8. 【bzoj4540】[Hnoi2016]序列 单调栈+离线+扫描线+树状数组区间修改区间查询

    题目描述 给出一个序列,多次询问一个区间的所有子区间最小值之和. 输入 输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数.接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i ...

  9. 【poj2155】Matrix(二维树状数组区间更新+单点查询)

    Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the ...

随机推荐

  1. 【洛谷1337】[JSOI2004] 吊打XXX(模拟退火经典题)

    点此看题面 大致题意: 一个平面上有\(n\)个点,每个点有1个权值,现在要选择平面上的一个点,使这\(n\)个点的权值乘上到达选定点的距离之和最小. 模拟退火 我们可以用模拟退火来做这道题. 先将\ ...

  2. mongodb索引 全文索引

    全文索引,也叫文本索引,平时,我们百度的搜索,比如api文档的搜索,这种全局的索引就可以使用全文索引实现 全文索引:对字符串与字符串数组创建全文可搜索对索引 使用情况:比如有一个数据集合,存储了用户的 ...

  3. CSS布局--垂直水平居中

    ···设置两个盒子 <div class="parent"> <div class="child"> </div></ ...

  4. IOS 网络-深入浅出(一 )

    首要我们以最为常用的UIImageView为例介绍实现原理: 1)UIImageView+WebCache:  setImageWithURL:placeholderImage:options: 先显 ...

  5. Matlab 中实用数据结构之 containers.Map

    概要   熟悉 Python 的都知道字典 Dict 类型数据结构功能的强大,Matlab 中虽然有表结构,但是其列名必须是亦变量名类型的字符串,如果我想用数字开头的字符串作键值,其表结构就无能为力了 ...

  6. vue中v-show和v-if的异同

    一.官方解释: v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建. v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次 ...

  7. java Html&JavaScript面试题:判断第二个日期比第一个日期大

    如何用脚本判断用户输入的的字符串是下面的时间格式2004-11-21 必须要保证用户的输入是此格式,并且是时间,比如说月份不大于12等等,另外我需要用户输入两个,并且后一个要比前一个晚,只允许用JAV ...

  8. PAT 乙级 1024

    题目 题目地址:PAT 乙级 1024 题解 模拟题,重点需要考虑到各种不同情况:简单来说一下: 因为输入格式固定,所以把不同的部分分别存储和处理可以在很大程度上简化运算:其中需要考虑最多的就是小数部 ...

  9. python 使用requests 请求 https 接口 ,取消警告waring

    response = requests.request("POST", url, timeout=20, data=payload, headers=headers, proxie ...

  10. django1.11文档 模型重点笔记

    模型最重要的属性是Manager. 它是Django 模型进行数据库查询操作的接口,并用于从数据库提取实例. 如果没有自定义Manager,则默认的名称为objects. Managers 只能通过模 ...