[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 ...
随机推荐
- 国产操作系统深度deepin V20体验
1. 安装系统 国产操作系统deepin V20 bata版本已经发布.本人第一时间安装和体验.在犹豫很久之后,因为受到最新内核,高版本的bash和Python的诱惑,字体更加和谐等因素,选择升级系统 ...
- Springboot:第一个Springboot程序(一)
1.创建Springboot项目 选择创建Springboot项目: 填写项目基本信息: 选择Springboot版本以及web依赖(内嵌tomcat): 创建完成: 创建完成后 等待构建maven项 ...
- iview使用之怎样给Page组件添加跳转按钮
在项目开发过程中,我们会经常遇到使用分页的表格,然而在ivieiw中,我们通常只能使用Page组件自带的功能,如下图: 切换每页条数这些基本的功能都不说了,有时候我们需要在输入框里输入想要跳转到的页数 ...
- windows下部署.netcore+docker系列四 (部署程序,重点就要来了)
前面的都是为这章做准备,加油把骚年们 PS:C# 项目可以按照流程一步步来,java 偶然其他的可以找下其他的网上资源 1.在 VS2019中 添加docker 支持 (其实也就是追加一个Docker ...
- jquery动态live绑定toggle事件
$(".btn").live("click",function(){ $(this).toggle( function () { //事件 1 console. ...
- php 推荐密码加密的方法
password_hash() 函数 password_hash() 函数用于创建密码的散列(hash) PASSWORD_DEFAULT - 使用 bcrypt 算法 (PHP 5.5.0 默认). ...
- 微信小程序 POST传值跳坑
来源:https://www.cnblogs.com/ordinaryk/p/8430462.html 加这个就行了: header : { 'content-type': 'application/ ...
- shell脚本之awk(一)
运维必备技能 概述: 1.awk是一种编程语言,用于linux/unix下对文本和数据进行扫描.处理数据来源:标准输入.文件.管道. 2.linux中常用的awk编译器版本有mawk,gawk.R ...
- CG-CTF(4)
CG-CTF https://cgctf.nuptsast.com/challenges#Web 续上~ 第十六题:bypass again 代码分析: 当a不等于b,且a和b的md5值相同时,才会返 ...
- Linux网络服务第六章PXE高效能批量网络装机
1.IP地址配置 2.关闭防火墙以及selinux状态如下 systemctl stop firewalld Iptables -F Setenforce 0 三.部署FTP服务 1.安装F ...