HDU - Segment Tree with Pruning

2023-10-30 12:50
文章标签 tree hdu segment pruning

本文主要是介绍HDU - Segment Tree with Pruning,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=6983

Problem Description
Chenjb is struggling with data stucture now. He is trying to solve a problem using segment tree. Chenjb is a freshman in programming contest, and he wrote down the following C/C++ code and ran ‘‘Node* root = build(1, n)’’ to build a standard segment tree on range [1,n]:

Node* build(long long l, long long r) {
Node* x = new(Node);
if (l == r) return x;
long long mid = (l + r) / 2;
x -> lchild = build(l, mid);
x -> rchild = build(mid + 1, r);
return x;
}

Chenjb submitted his code, but unfortunately, got MLE (Memory Limit Exceeded). Soon Chenjb realized that his program will new a large quantity of nodes, and he decided to reduce the number of nodes by pruning:

Node* build(long long l, long long r) {
Node* x = new(Node);
if (r - l + 1 <= k) return x;
long long mid = (l + r) / 2;
x -> lchild = build(l, mid);
x -> rchild = build(mid + 1, r);
return x;
}

You know, Chenjb is a freshman, so he will try different values of k to find the optimal one. You will be given the values of n and k, please tell him the number of nodes that will be generated by his new program.

Input
The first line contains a single integer T ( 1 ≤ T ≤ 10000 ) T (1≤T≤10000) T(1T10000), the number of test cases. For each test case:

The only line contains two integers n and k ( 1 ≤ k ≤ n ≤ 1 e 18 ) (1≤k≤n≤1e18) (1kn1e18), denoting a query.

Output
For each query, print a single line containing an integer, denoting the number of segment tree nodes.

Sample Input
3
100000 1
100000 50
1000000000000000000 1

Sample Output
199999
4095
1999999999999999999

题目大意:
给定整数区间[1,n]和一个整数k,判断题目给定的第二段代码由该区间生成的对应的树有多少个节点

思路:
首先题目生成这棵树的过程可以稍微转化一下,我们只需要区间的长度,而不需要区间的两界。根据代码逻辑,也就是,给定区间长度为n,最终会生成一颗叶子节点对应长度不超过k的总共有若干个节点树。对于每一个节点的区间长度r,如果r大于k,那么它必不是叶子节点,那么它会连接有两个子节点。
当r为奇数时,其连接的左节点长度为 r / 2 + 1 r/2 + 1 r/2+1,右节点长度为 r / 2 r/2 r/2,如果是偶数,则长度都为 r / 2 r/2 r/2.

我们可以修改一下其给定的代码,从而以 O ( l o g n ) O(logn) O(logn)效率得到其生成出来的树所达到的最大深度。当其深度为D时,对于其1~D-1的所有层,必然都是满的,而D层则未知。
我们假定一个小于n的整数P,其生成出来的树恰好是具有D-1层的完全二叉树,并且叶子节点长度都为k。当我们将P,逐渐+1,在其+1的过程中,会发现一个规律:对于一个节点,每次加入一个1,这个1都会自上而下将其 “区间长度最小的、最靠左的”对应子节点+1。如下图:

在这里插入图片描述
在这里插入图片描述
原本叶子节点都是所能取的最大长度k,而+1之后,超过了这个值,那么就会衍生出两个叶子节点。
而这样的+1操作,我们需要执行n-P次,我们如何确定最后的结果?

我们需要统计,P的时候,其叶子节点有几个,当我们要执行的操作小于等于这个初叶子节点数时(假设为V个吧),我们最终会增加2V个叶子节点(每次操作都会衍生两个),而当我们进行的操作大于这个V时,则至多也只能增加2V个叶子节点,因为后续的操作只是在“填充”现有叶子节点的长度罢了,却不可能再使其超过给定k了(因为我们一开始就求得其最大深度为D了呀,如果还能超过,说明我们D求的不对)。这样我们就能通过预处理不同层数满时该层对应的节点数目与不同层数的满二叉树节点数,比较快速地求出所有样例对应的结果。(一开始因为直接用pow()求对应的2的高次幂前缀和时产生了一些未知问题,数据大于一定范围的时候,其求出结果比实际结果大1,也导致了比赛时最终没能写出这道题)

AC代码:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
long long n, k;
long long ci[1000006];
long long deep(long long l, long long r) {if (r - l + 1 <= k)return 1;return deep(l, (l + r) / 2) + 1;
}
int main(void) {ci[1] = 1;long long it = 2;for (int i = 2; i <= 64; i++) {ci[i] += ci[i - 1] + it;it *= 2;}long long t; cin >> t;while (t--) {cin >> n >> k;if (n <= k) {cout << 1 << endl; continue;}long long Deep = deep(1, n);long long now = ci[Deep - 1];long long temp = (long long)pow(2, Deep - 2);long long yu = n - temp * k;now += min(temp * 2, yu * 2);cout << now << endl;}
}

这篇关于HDU - Segment Tree with Pruning的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/308037

相关文章

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

hdu 3790 (单源最短路dijkstra)

题意: 每条边都有长度d 和花费p,给你起点s 终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。 解析: 考察对dijkstra的理解。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstrin

hdu 2489 (dfs枚举 + prim)

题意: 对于一棵顶点和边都有权值的树,使用下面的等式来计算Ratio 给定一个n 个顶点的完全图及它所有顶点和边的权值,找到一个该图含有m 个顶点的子图,并且让这个子图的Ratio 值在所有m 个顶点的树中最小。 解析: 因为数据量不大,先用dfs枚举搭配出m个子节点,算出点和,然后套个prim算出边和,每次比较大小即可。 dfs没有写好,A的老泪纵横。 错在把index在d

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时