花神游历各国(洛谷:线段树区间开方)

2024-05-25 04:04

本文主要是介绍花神游历各国(洛谷:线段树区间开方),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

主要还是注意到两个点,一个是区间最大值为小于等于1的时候再怎么开方都是1所以不用修改,第二个点事数据范围是1e12,开方六次区间最大值就会变为1,当一个区间修改超过六次就返回.

using i64 = long long;
using ll = long long;
constexpr ll inf = 1e18;
struct Info {ll sum = 0;ll max = -inf;void apply(const Info& v) {sum = sum + v.sum;max = max + v.max;}
};
Info operator+(Info a, Info b) {Info ans{};ans.sum = a.sum + b.sum;ans.max = std::max(a.max, b.max);return ans;
}

这个题区间开方不好使用懒标记所以只能用普通线段树,每个线段维护三个信息,区间最大值,区间和,区间被修改了几次.

template<class Info>
struct SegmentTree {int n;std::vector<Info> info;SegmentTree() : n(0) {}SegmentTree(int n_, Info v_ = Info()) {init(n_, v_);}template<class T>SegmentTree(std::vector<T> init_) {init(init_);}void init(int n_, Info v_ = Info()) {init(std::vector<Info>(n_, v_));}template<class T>void init(std::vector<T> init_) {n = init_.size();info.assign(4 << (int)std::log2(n), Info());std::function<void(int, int, int)> build = [&](int p, int l, int r) {if (r - l == 1) {info[p] = init_[l];return;}int m = (l + r) / 2;build(2 * p, l, m);build(2 * p + 1, m, r);pull(p);};build(1, 0, n);}void pull(int p) {info[p] = info[2 * p] + info[2 * p + 1];}void modify(int p, int l, int r, int x, const Info& v) {if (r - l == 1) {info[p] = v;return;}int m = (l + r) / 2;if (x < m) {modify(2 * p, l, m, x, v);}else {modify(2 * p + 1, m, r, x, v);}pull(p);}void modify(int p, const Info& v) {modify(1, 0, n, p, v);}Info rangeQuery(int p, int l, int r, int x, int y) {if (l >= y || r <= x) {return Info();}if (l >= x && r <= y) {return info[p];}int m = (l + r) / 2;return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);}Info rangeQuery(int l, int r) {return rangeQuery(1, 0, n, l, r);}};

jiangly普通线段树的板子是没有区间修改的,我把区间修改单独拿出来讲

    void rangeApply(int p, int l, int r, int x, int y) {if (l >= y || r <= x)return;//如果一个线段修改超过六次,就返回因为全部都是1了//又或者说一个线段的最大值为1if (info[p].max <= 1)return;if (r - l == 1) {info[p].sum = std::sqrt(info[p].sum);info[p].max = info[p].sum;return;}int m = (l + r) / 2;rangeApply(2 * p, l, m, x, y);rangeApply(2 * p + 1, m, r, x, y);pull(p);}void rangeApply(int l, int r) {return rangeApply(1, 0, n, l, r);}

第一个返回条件,标准的不在修改范围内返回,第二个条件,修改次数大于等于6了,或者区间最大值小于等于1了,返回,第三个条件,当递归到1的时候对该点的值开方,并更新该点的最大值(我就是在这里卡了,忘记更新最大值了),返回,其它线段就往子线段递归,最后pull合并.

int main() {int n;std::cin >> n;std::vector<Info>a(n);for (int i = 0; i < n; i++) {std::cin >> a[i].sum;a[i].max = a[i].sum;}SegmentTree<Info> tree(a);int m;std::cin >> m;while (m--) {ll op, x, y;std::cin >> op >> x >> y;if (x > y)std::swap(x, y);x--;if (op == 0) {tree.rangeApply(x, y);}else {std::cout<<tree.rangeQuery(x, y).sum<<'\n';}}return 0;
}

 主函数部分的逻辑很简单不多说了.

这篇关于花神游历各国(洛谷:线段树区间开方)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

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

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

圆与线段的交点

poj 3819  给出一条线段的两个端点,再给出n个圆,求出这条线段被所有圆覆盖的部分占了整条线段的百分比。 圆与线段的交点 : 向量AB 的参数方程  P = A + t * (B - A)      0<=t<=1 ; 将点带入圆的方程即可。  注意: 有交点 0 <= t <= 1 ; 此题求覆盖的部分。 则 若求得 t  满足 ; double ask(d

hdu4267区间统计

题意:给一些数,有两种操作,一种是在[a,b] 区间内,对(i - a)% k == 0 的加value,另一种操作是询问某个位置的值。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import