[csu/coj 1080]划分树求区间前k大数和
题意:从某个区间内最多选择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大数和的更多相关文章
- [hdu2665]Kth number(划分树求区间第k大)
解题关键:划分树模板题. #include<cstdio> #include<cstring> #include<algorithm> #include<cs ...
- HDU 3473 Minimum Sum (划分树求区间第k大带求和)(转)
题意:在区间中找一个数,求出该区间每个数与这个数距离的总和,使其最小 找的数字是中位数(若是偶数个,则中间随便哪个都可)接着找到该区间比此数大的数的总和 区间中位数可以使用划分树,然后在其中记录:每层 ...
- HDOJ题目4417 Super Mario(划分树求区间比k小的个数+二分)
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- 线段树维护区间前k小
线段树维护区间前k小 $ solution: $ 觉得超级钢琴太麻烦?在这里线段树提供一条龙服务 . 咳咳,开始讲正题!这道题我们有一个和超级钢琴复杂度一样 $ ~O(~\sum x\times lo ...
- hdu4417 主席树求区间小于等于K
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 Problem Description Mario is world-famous plum ...
- 主席树——求区间第k个不同的数字(向右密集hdu5919)
和向左密集比起来向右密集只需要进行小小的额修改,就是更新的时候从右往左更新.. 自己写的被卡死时间.不知道怎么回事,和网上博客的没啥区别.. /* 给定一个n个数的序列a 每次询问区间[l,r],求出 ...
- [poj2104]kth-number(归并树求区间第k大)
复杂度:$O(nlog^3n)$ #include<cstdio> #include<cstring> #include<algorithm> #include&l ...
- 主席树--动态区间第k小
主席树--动态区间第\(k\)小 模板题在这里洛谷2617. 先对几个问题做一个总结: 阅读本文需要有主席树的基础,也就是通过区间kth的模板题. 静态整体kth: sort一下找第k小,时间复杂度\ ...
- 2016年湖南省第十二届大学生计算机程序设计竞赛---Parenthesis(线段树求区间最值)
原题链接 http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1809 Description Bobo has a balanced parenthes ...
随机推荐
- ajax ★ ★ ★ ★ ★
ajax 1 定义: 是创建交互式应用的网页交互技术 2 特点:无刷新.异步 3 中介数据类型: 1) XML - 可扩展的标记语言 ...
- 如何将Python项目发布到PyPI
The Python Package Index (PyPI) is a repository of software for the Python programming language. 如何打 ...
- jest enzyme unit test react
1. 测试类型 单元测试:指的是以原件的单元为单位,对软件进行测试.单元可以是一个函数,也可以是一个模块或一个组件,基本特征就是只要输入不变,必定返回同样的输出.一个软件越容易些单元测试,就表明它的模 ...
- 从"UDF不应有状态" 切入来剖析Flink SQL代码生成
从"UDF不应有状态" 切入来剖析Flink SQL代码生成 目录 从"UDF不应有状态" 切入来剖析Flink SQL代码生成 0x00 摘要 0x01 概述 ...
- openssl查看证书细节 [转载]
openssl x509部分命令 打印出证书的内容: openssl x509 -in cert.pem -noout -text 打印出证书的系列号 openssl x509 -in cert.pe ...
- 日志分析工具ELK(三)
目前官网更新特别快,不到半年时间就更新了好几个版本,目前最新的是5.1 以下安装配置使用4.5版本的 https://www.elastic.co/guide/en/kibana/4.5/index. ...
- Linux Centos7(Mac)安装Docker
docker 强调隔离性 docker:官网 docker:镜像官网: 镜像官网可以所有应用,选择安装环境:会给出安装命令,例如:docker pull redis 默认拉取最新的版本( ...
- 【小技巧】【App store切换为中文】
为什么80%的码农都做不了架构师?>>> 贡献作者 -[XJDomain]博客XJ: https://my.oschina.net/shengbingli/blogGitHub ...
- Red 编程语言 2019 开发计划:全速前进!
开发四年只会写业务代码,分布式高并发都不会还做程序员? >>> Red 编程语言开发团队昨日发布了一篇 "Full steam ahead" 的文章,对其 2 ...
- 几年前的今天,Google发了这几篇“大”新闻
免责声明: 因阅读本文所导致的任何时间或经济上的损失,皆由您自行承担,本小编概不负责. 估计今天我的朋友圈会被"震惊!"刷屏,来看看 Google 做过哪些令人"震惊&q ...