题意:从某个区间内最多选择k个数,使得和最大

思路:首先题目给定的数有负数,如果区间前k大出现负数,那么负数不选和更大,于是对于所有最优选择,负数不会出现,所以用0取代负数,问题便转化为区间的前k大数和。

划分树:

[1  6  3  8  5  4  7  2]

[6  8  5  7][1  3  4  2]

[8  7][6  5][3  4][1  2]

[8][7][6][5][4][3][2][1]

把快排的结果从上至下依次放入线段树,就构成了划分树,划分的意思就是选定一个数,把原序列分成两块,使得左边整体大于右边,而一个块内的数在原序列的相对位置不发生变化。划分树的过程基本就是初始化,建树,和查找。初始化只要把原序列导入划分树的根就行了,建树过程依赖排好序的原序列来得到用来“划分”的数,查找过程也很简单,不需要像线段树那样将询问分解,在树上查找答案的时候是左右子树二选一。

划分树一般用来求区间第k大数,对于划分树为什么可以求区间k大和,见代码。

 #pragma comment(linker, "/STACK:10240000,10240000")

 #include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <map>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <ctime>
#include <cctype>
#include <set>
#include <bitset>
#include <functional>
#include <numeric>
#include <stdexcept>
#include <utility> using namespace std; #define mem0(a) memset(a, 0, sizeof(a))
#define mem_1(a) memset(a, -1, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define rep_up0(a, b) for (int a = 0; a < (b); a++)
#define rep_up1(a, b) for (int a = 1; a <= (b); a++)
#define rep_down0(a, b) for (int a = b - 1; a >= 0; a--)
#define rep_down1(a, b) for (int a = b; a > 0; a--)
#define all(a) (a).begin(), (a).end()
#define lowbit(x) ((x) & (-(x)))
#define constructInt4(name, a, b, c, d) name(int a = 0, int b = 0, int c = 0, int d = 0): a(a), b(b), c(c), d(d) {}
#define constructInt3(name, a, b, c) name(int a = 0, int b = 0, int c = 0): a(a), b(b), c(c) {}
#define constructInt2(name, a, b) name(int a = 0, int b = 0): a(a), b(b) {}
#define pchr(a) putchar(a)
#define pstr(a) printf("%s", a)
#define sstr(a) scanf("%s", a)
#define sint(a) scanf("%d", &a)
#define sint2(a, b) scanf("%d%d", &a, &b)
#define sint3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define pint(a) printf("%d\n", a)
#define test_print1(a) cout << "var1 = " << a << endl
#define test_print2(a, b) cout << "var1 = " << a << ", var2 = " << b << endl
#define test_print3(a, b, c) cout << "var1 = " << a << ", var2 = " << b << ", var3 = " << c << endl
#define mp(a, b) make_pair(a, b)
#define pb(a) push_back(a) typedef unsigned int uint;
typedef long long LL;
typedef pair<int, int> pii;
typedef vector<int> vi; const int dx[] = {, , -, , , , -, -};
const int dy[] = {-, , , , , -, , - };
const int maxn = 1e4 + ;
const int md = 1e9 + ;
const int inf = 1e9 + ;
const LL inf_L = 1e18 + ;
const double pi = acos(-1.0);
const double eps = 1e-; template<class T>T gcd(T a, T b){return b==?a:gcd(b,a%b);}
template<class T>bool max_update(T &a,const T &b){if(b>a){a = b; return true;}return false;}
template<class T>bool min_update(T &a,const T &b){if(b<a){a = b; return true;}return false;}
template<class T>T condition(bool f, T a, T b){return f?a:b;}
template<class T>void copy_arr(T a[], T b[], int n){rep_up0(i,n)a[i]=b[i];}
int make_id(int x, int y, int n) { return x * n + y; } /// 划分树求区间前k大和
/// 关于为什么可以用前缀和来求前k大的和:对于一个询问区间,如果前k大在左子树,
/// 那么这k个数会按原顺序依次进入左子树,进入左子树后它们之间不会有其它不是前k大的数,
/// 所以对应到下一层也是连续的一段,于是可用前缀和来得到,对于前k大跨区间的情况同理。
/// 区间范围为:1 ~ n
struct PartitionTree {
int val[][maxn], sum[][maxn], cnt[][maxn];//划分后的结果,前缀和,进入左子树的个数
void init(int a[], int l, int r) {
mem0(val);
mem0(sum);
mem0(cnt);
for (int i = l; i <= r; i ++) sum[][i] = sum[][i - ] + (val[][i] = a[i]);
}
/// 向下更新val和sum数组,同时维护cnt数组的值
void build(int b[], int l, int r, int dep) {
if (l == r) return ;
int m = (l + r) >> ;
/// c记录大于中位数的数的个数,cc记录进入左子树的等于中位数的数的个数,由于相等情况的存在,这个信息必不可少。
int lc = , rc = , lt = (r - l + ) >> , c = , cc = ;
for (int i = l; i <= r; i ++) c += val[dep][i] > b[m];
for (int i = l; i <= r; i ++) {
cnt[dep][i] = cnt[dep][i - ];
if (lc < lt && (val[dep][i] > b[m] || val[dep][i] == b[m] && cc < lt - c)) {
val[dep + ][l + lc ++] = val[dep][i];
cnt[dep][i] ++;
if (val[dep][i] == b[m]) cc ++;
}
else {
val[dep + ][m + + rc ++] = val[dep][i];
}
} for (int i = l; i <= r; i ++) sum[dep + ][i] = sum[dep + ][i - ] + val[dep + ][i];
build(b, l, m, dep + );
build(b, m + , r, dep + );
}
int query_ksum(int L, int R, int k, int l, int r, int dep) {
if (k == ) return ;
if (l == r) return val[dep][l];
int m = (l + r) >> , c = cnt[dep][R] - cnt[dep][L - ];
if (c >= k) {
int x = cnt[dep][L - ] - cnt[dep][l - ], y = cnt[dep][R] - cnt[dep][l - ];
return query_ksum(l + x, l + y - , k, l, m, dep + );
}
else {
int x0 = cnt[dep][L - ] - cnt[dep][l - ], x = L - l - cnt[dep][L - ] + cnt[dep][l - ], y = R - l + - cnt[dep][R] + cnt[dep][l - ];
return sum[dep + ][l + x0 - + c] - sum[dep + ][l + x0 - ] + query_ksum(m + + x, m + + y - , k - c, m + , r, dep + );
}
} };
PartitionTree pt;
pii node[maxn];
int a[maxn], b[maxn], p[maxn]; bool cmp(int i, int j) {
return i > j;
}
int main() {
//freopen("in.txt", "r", stdin);
int n, m;
while (cin >> n) {
rep_up0(i, n) {
sint2(node[i].first, node[i].second);
max_update(node[i].second, );
}
sort(node, node + n);
rep_up0(i, n) {
b[i] = a[i] = node[i].second;
p[i] = node[i].first;
}
sort(b, b + n, cmp);
pt.init(a - , , n);
pt.build(b - , , n, );
cin >> m;
rep_up0(i, m) {
int l, r, k;
sint3(l, r, k);
l = lower_bound(p, p + n, l) - p + ;
r = upper_bound(p, p + n, r) - p;
min_update(k, r - l + );
printf("%d\n", pt.query_ksum(l, r, k, , n, ));
}
cout << endl;
}
return ;
}

[csu/coj 1080]划分树求区间前k大数和的更多相关文章

  1. [hdu2665]Kth number(划分树求区间第k大)

    解题关键:划分树模板题. #include<cstdio> #include<cstring> #include<algorithm> #include<cs ...

  2. HDU 3473 Minimum Sum (划分树求区间第k大带求和)(转)

    题意:在区间中找一个数,求出该区间每个数与这个数距离的总和,使其最小 找的数字是中位数(若是偶数个,则中间随便哪个都可)接着找到该区间比此数大的数的总和 区间中位数可以使用划分树,然后在其中记录:每层 ...

  3. HDOJ题目4417 Super Mario(划分树求区间比k小的个数+二分)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  4. 线段树维护区间前k小

    线段树维护区间前k小 $ solution: $ 觉得超级钢琴太麻烦?在这里线段树提供一条龙服务 . 咳咳,开始讲正题!这道题我们有一个和超级钢琴复杂度一样 $ ~O(~\sum x\times lo ...

  5. hdu4417 主席树求区间小于等于K

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417   Problem Description Mario is world-famous plum ...

  6. 主席树——求区间第k个不同的数字(向右密集hdu5919)

    和向左密集比起来向右密集只需要进行小小的额修改,就是更新的时候从右往左更新.. 自己写的被卡死时间.不知道怎么回事,和网上博客的没啥区别.. /* 给定一个n个数的序列a 每次询问区间[l,r],求出 ...

  7. [poj2104]kth-number(归并树求区间第k大)

    复杂度:$O(nlog^3n)$ #include<cstdio> #include<cstring> #include<algorithm> #include&l ...

  8. 主席树--动态区间第k小

    主席树--动态区间第\(k\)小 模板题在这里洛谷2617. 先对几个问题做一个总结: 阅读本文需要有主席树的基础,也就是通过区间kth的模板题. 静态整体kth: sort一下找第k小,时间复杂度\ ...

  9. 2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)

    原题链接 http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1809 Description Bobo has a balanced parenthes ...

随机推荐

  1. NCTF2019 小部分题解

    前言 礼拜五领航杯打的比较累,做不出WEB,D3CTF没用,做了NJCTF的一些题目(懒,睡觉到12点起) Misc 第一次比赛先去做misc,以前一直做WEB,以后要WEB+MISC做.礼拜六下午做 ...

  2. python os模块获取指定目录下的文件列表

    bath_path = r"I:\ner_results\ner_results" dir_list1 = os.listdir(bath_path) for dir1 in di ...

  3. search(7)- elastic4s-search-filter模式

    现在我们可以开始探讨ES的核心环节:搜索search了.search又分filter,query两种模式.filter模式即筛选模式:将符合筛选条件的记录作为结果找出来.query模式则分两个步骤:先 ...

  4. TIME_WAIT的危害与避免

    time-wait的产生: 在TCP连接中四次挥手关闭连接时,主动关闭连接的一方(上图中时Client)会在发送最后一条ACK报文后维持一段时长2MSL(MSL指的是数据包在网络中的最大生存时间)的等 ...

  5. 数据挖掘入门系列教程(十一)之keras入门使用以及构建DNN网络识别MNIST

    简介 在上一篇博客:数据挖掘入门系列教程(十点五)之DNN介绍及公式推导中,详细的介绍了DNN,并对其进行了公式推导.本来这篇博客是准备直接介绍CNN的,但是想了一下,觉得还是使用keras构建一个D ...

  6. Golang Map实现(四) map 的赋值和扩容

    title: Golang Map 实现 (四) date: 2020-04-28 18:20:30 tags: golang map 操作,是map 实现中较复杂的逻辑.因为当赋值时,为了减少has ...

  7. Spring Boot JPA 中transaction的使用

    文章目录 @Transactional的实现 @Transactional的使用 Transaction的传播级别 REQUIRED SUPPORTS MANDATORY NEVER NOT_SUPP ...

  8. vue + ArcGIS 地图应用系列一:arcgis api本地部署(开发环境)

    封面 1. 下载 ArcGIS API for JavaScript 官网地址: https://developers.arcgis.com/javascript/3/ 下载地址:http://lin ...

  9. JavaScript面向对象的作用域链(转载)

    JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕... 一.“JavaScript中无块级作用域” 在Java或C# ...

  10. Eclipse Mac OS版 卸载svn插件subclipse

    点击Eclipse -> About Eclipse 在打开的窗口中点击Installation Details(安装细节) 在Installed Software标签窗口中,选中Subclip ...