[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 ...
随机推荐
- Ubuntu安装Elasticsearch6.3
本文使用的 Ubuntu 版本信息: Distributor ID: Ubuntu Description: Ubuntu LTS Release: 16.04 Codename: xenial 1. ...
- 你知道什么是 GitHub Action 么?
本文是 GitHub Action 的入门教程,如您已有相关使用经验可以直接关掉. GitHub Action 是 GitHub 于 2018 年 10 月推出的一个 CI\CD 服务. 之前一直都是 ...
- SpringCloud-Gateway 网关路由、断言、过滤
Gateway 简介 是什么? Spring Cloud 全家桶中有个很重要的组件:网关.在 1.x 版本中使用的是 Zuul 网关,但是到了 2.x,由于Zuul的升级不断跳票,Spring Clo ...
- ADO.Net和Entity Framework的区别联系
它们有以下几点区别:1,ADO.Net是开发人员自己select.update等写sql语句,来实现对数据库的增删改查等操作:采用EF进行开发操作数据库的时候,只需要操作对象,这样做使开发更方便,此时 ...
- C# 反射(一)
刚工作没多久,需要对以往的知识进行复习和巩固.先讲讲最近在公司常用到的反射机制. 反射(Reflection):提供了封装程序集.模块和类型的对象(Type 类型).可以使用反射动态创建类型的实例,将 ...
- ES6的 Iterator 遍历器到底是什么?
这节课要讲的是ES6中的Iterator. for...of为啥不遍历Object对象 第十三节我们讲了简单又实用的for...of,我们可以使用它来遍历数组,字符串,Set和Map结构,但是有没有发 ...
- 总结php删除html标签和标签内的内容的方法
来源:https://www.cnblogs.com/shaoguan/p/7336984.html 经常扒别人网站文章的坑们:我是指那种批量式采集的压根不看内容的:少不了都会用到删除html标签的函 ...
- CG-CTF(5)
CG-CTF https://cgctf.nuptsast.com/challenges#Web 续上~ 第二十二题:SQL注入1 点击Source: 分析: mysql_select_db()函数: ...
- 理解分布式一致性:拜占庭容错与PBFT
理解分布式一致性:拜占庭容错与PBFT 拜占庭问题 拜占庭容错BFT PBFT(Practical Byzantine Fault Tolerance) why 3f+1 ? PBFT 的优点 PBF ...
- SpringBoot应用操作Rabbitmq(topic交换器高级操作)
一.topic交换器为主题交换器,可以根据路由key模糊匹配 实现模型图 二.实战 1.引入maven <dependency> <groupId>org.springfram ...