线段树

AtCoder ABC 157E Simple String Queries

六月ゝ 毕业季﹏ 提交于 2020-03-07 19:14:30
题目链接: https://atcoder.jp/contests/abc157/tasks/abc157_e 题目大意   给定一串全由小写英文字母组成的字符串,然后顺序给出$Q$个操作,一种为替换字符串中的某个字符;另一种为查询字符串某个区间里面有多少个不同的字符。要求顺序输出第二种操作的结果。 分析   线段树单点更新,每个节点存一个32位整数,其中26位代表26个英文字母,更新的话只要节点求或就行了。   其他方法1:用26个树状数组,这个原理是一样的,但总感觉没有只用一棵线段树简明。   其他方法2:把每个字母出现的位置存到有序表里面,然后查询的时候利用二分看看字母在不在区间里面,在的话就加一。   以上方法时间复杂度都为$O(QlogN)$,我选用的是一棵线段树的做法。 代码如下 1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 5 /*-------------------Define Start-------------------*/ 6 typedef bool BL; // 布尔类型 7 typedef char SB; // 有符号1字节,8位 8 typedef unsigned char UB; // 无符号1字节,8位 9 typedef

HDU 5443 The Water Problem 线段树找区间内最大值

一曲冷凌霜 提交于 2020-03-07 07:08:00
HDU 5443 The Water Problem 解题思路:线段树找区间内最大值,线段树基础题,这题用来练练敲线段树板子还是挺不错的。 # include <iostream> # include <math.h> # include <iomanip> # include <algorithm> # include <iostream> # include <math.h> # include <iomanip> # include <algorithm> # include <queue> # include <cstring> # include <string> # include <map> # include <stack> # include <stdio.h> # include <cstdio> # include <stdlib.h> # include <fstream> # include <iomanip> # pragma warning(disable:4996) # define INF 0x3f3f3f3f # define ll long long # define PI acos(-1.0) const int N = 1000010 ; const int maxn = 1e9 ; using namespace std ; int

题解 SP2916 【GSS5 - Can you answer these queries V】

£可爱£侵袭症+ 提交于 2020-03-06 18:11:58
前言 最近沉迷于数据结构,感觉数据结构很有意思。 正文 分析 先来分类讨论一下 1. \(x2<y1\) 如果 \(y1<x2\) 的话,答案 \(=\max \limits_{ y1 \leq x \leq y2} \{ s_i \} - \min \limits_{x1 \leq x \leq x2} \{ s_i \}\) 其中 \(s_i\) 表示 \(\sum\limits_{j=1}^{i} a_j\) ,就是俗称的前缀和 这个东西可以用线段树维护。 2. \(x2>=y1\) 这个怎么处理呢 答案 \(=\) \(\max \begin{cases}\max \limits_{ y1 \leq x \leq y2} \{ s_i \} - \min \limits_{x1 \leq x \leq y1} \{ s_i \}\\ \max \limits_{ x2 \leq x \leq y2} \{ s_i \} - \min \limits_{x1 \leq x \leq x2} \{ s_i \}\\ f(x,y)\end{cases}\) 这里的 \(f\) 函数就是最大子段和。 不会用线段树求最大子段和的可以看 这里 最上面的 \(2\) 个也是可以用线段树来维护的。 代码 最后要注意的一点是前缀和 ,我们知道区间 \([i,j]\) 的和 $ = s_j-s

@loj - 2987@ 「CTSC2016」时空旅行

谁说胖子不能爱 提交于 2020-03-04 12:34:33
目录 @description@ @solution@ @accepted code@ @details@ @description@ 2045 年,人类的技术突飞猛进,已经找到了进行时空旅行的方法。小 R 得到了一台时空旅行仪,他想用它调查不同时空中人类的发展状况。 根据平行时空理论,宇宙中存在着很多独立的时空,每个时空在下一个时间点还会分化出若干个不同的时空。宇宙是一个三维空间,人类使用空间直角坐标系来描述空间中的一个位置,三维坐标分别是 x,y,z。 我们假设在初始的时空(编号为 0)中,人类存在于地球上(地球的坐标为 (0,0,0)),其他的时空都是从一个现有的时空发展而来的。一个时空发生一个时间之后会发展成为另外一个时空(原来的时空不发生任何变化)。会影响小 R 的时间包括两类: 人类殖民了一个新的星球,该星球的状态变成“已被殖民”。 人类放弃了一个已被殖民的星球,该星球的状态变成“未被殖民”。 每次进行时空旅行时,小 R 会先选定一个时空。在这个时空中,人类已经殖民了一些星球。小 R 只要到达该时空中任意一个已被殖民的星球,就能调查人类的发展状况。 小 R 的时空旅行仪出现了一些问题,调整 x 坐标的按钮坏掉了,因此到达点的 x 坐标被固定了(每次旅行的 x 坐标值可能不同)。与此同时,他仍能任意调整到达点的 y 坐标和 z 坐标。 这个问题大大增大了小 R 的花费

树状数组

生来就可爱ヽ(ⅴ<●) 提交于 2020-03-02 10:17:05
树状数组可以看作线段树的变形,不同于线段树可以计算区间和,区间最大/最小值,树状数组一般只能完成以下操作: 给定一个初始值全为0的数列 a 1 . . . a n a_1...a_n a 1 ​ . . . a n ​ 给定 i i i ,计算 a 1 + a 2 + . . . + a i a_1+a_2+...+a_i a 1 ​ + a 2 ​ + . . . + a i ​ ,即前缀和 给定 i i i 和 x x x ,执行 a i + = x a_i+=x a i ​ + = x 。 基于线段树的实现 如果使用线段树执行上述功能,只需要对介绍过的 RMQ 进行少量的修改,就可以实现,线段树每个节点维护区间的和。 接下来,我们重点关注给定区间始末 s , t s,t s , t ,如何求得 a s + a s + 1 + . . . + a t a_s+a_{s+1}+...+a_t a s ​ + a s + 1 ​ + . . . + a t ​ ,对于线段树,我们直接进行区间的迭代查询即可。但是有时这个效率依然是不够的 此时,如果我们维护前缀和 s u m [ i ] = a 0 + . . . + a i sum[i]=a_0+...+a_i s u m [ i ] = a 0 ​ + . . . + a i ​ ,那么只需要计算 s u m [ t ] − s

poj 2528 Mayor's posters(线段树+离散化)

左心房为你撑大大i 提交于 2020-02-29 16:30:37
http://poj.org/problem?id=2528 (1)给定一条线段,依次在上面覆盖一些线段(左右位置已给定),求最终能看到的线段数(看到一部分也算)。 本题的本质和染色问题一样,成段染色,延迟更新。 (2)有一个难点在于,所给定的区域是1到1000万,不可能开这么大的数组。况且,用于覆盖的线段只有1万条。 这里用到了离散化方法,简化了问题,压缩了数组的大小(具体情况见代码)。 (3)傻到开了一个pos[]数组,准备用来表示谁映射到谁。。(很无语,当时没有注意pos[]数组要开到1000万这么大)。 (4)在写travel()函数时,忘了加上pushdown()。 (5)思路点拨:     1)映射(离散化),降下内存使用量;     2)染色操作;     3)先消除所有的延迟标记(travel),再计量最终的颜色数,输出结果。 (6)熟悉映射的C++写法。 具体代码: View Code #include<stdio.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #include<algorithm> #include<vector> #include<map> using namespace std; const int maxn=101000; int t, n, ans; int num

POJ - 1426 (Find The Multiple )

久未见 提交于 2020-02-29 15:52:21
题意:给定一个整数 n (<=200) 求它的一个倍数 m ,m全部由 1 或 0 组成; 分析:这题有三种解法; ①打表,预先处理出1~200的所有答案; ②因为答案最多只有18位,所以可以用BFS队列维护,数据用 long long 存就好; ③线段树思想; 前面两种应该很好想,说说第三种,我们以 n=6 为例子,则可以模拟一下求解过程; 先要知道同余模定理: (a*b)% n = (a%n*b%n)%n (a+b)%n = (a%n+b%n)%n 所以我们每次选择当前位之后都可以只留下取模之后的数: 左边的数每一个节点 a(b) 代表当前位置选择 a 后模上 n 的余数是 b;右边则是每个节点对应的线段树编号; 可以看出,我们每次先选 0 再选1 ,对应的线段树编号则是偶数为 0 ,奇数为 1 ; 这样我们可以模拟维护一个线段树找出答案; 代码: #include<queue> #include<cstdio> #include<iostream> using namespace std; int mod[1000000+10]; int n; int ans[100]; int main() { while(~scanf("%d",&n)&&n){ mod[1]=1%n; int i; // i 是模拟线段树节点编号 for(i=2;;i++){ mod[i]=(mod[i

线段树(一)

寵の児 提交于 2020-02-28 23:31:43
题目描述 如题,已知一个数列,你需要进行下面两种操作: 将某区间每一个数加上 k 。 求出某区间每一个数的和。 输入格式 第一行包含两个整数 n, , m,分别表示该数列数字的个数和操作的总个数。 第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。 接下来 m 行每行包含 3 或 4 个整数,表示一个操作,具体如下: 1 x y k :将区间 [x, y] 内每个数加上 k 。 2 x y :输出区间 [x, y] 内每个数的和。 输出格式 输出包含若干行整数,即为所有操作 2 的结果。 in: 5 5 1 5 4 2 3 2 2 4 1 2 3 2 2 3 4 1 1 5 1 2 1 4 out: 11 8 20 类似于树状数组,对数据操作有nlogn的性能 #include<bits/stdc++.h> #define N 1000010 using namespace std; struct node { int l,r; long long data; long long lazy; }tree[N<<1]; int a[N],n,m; void bulid(int num,int l,int r) { tree[num].l=l; tree[num].r=r; if(l==r) { tree[num].lazy=a[l]; return;

CF1303G(点分治+李超线段树)

随声附和 提交于 2020-02-28 20:31:23
CF1303G (点分治+李超线段树) 题目大意 给你一棵 n 个点的带点权的树,你需要求出树上的一个路径 \(x_1,x_2,\ldots,x_k,最大化 \sum_{i=1}^kia_{x_i}\) ,求最大权值。 数据范围 \(2\le n\le 150000\) 一条最大的路径并不好考虑如何直接求得, 我们试图把它用两条路径来合并 把一条过点 \(x_k\) 的路径 \(x_1, x_2, x_3\cdots x_{k-1},x_k ,x_{k+1}\cdots x_n\) 看成两条 \(x_1, x_2, x_3\cdots x_{k-1}\) 权值为 \(V_1\) 长度为 \(l_1\) , 和为 \(S_1\) \(x_k ,x_{k+1}\cdots x_n\) \(V_2, l_2, S_2\) 那么 \(V=V_1+V_2 + S_2*l_1\) 既然是可 \(O(1)\) 合并的, 又不难枚举, 可以想到用点分治来枚举中间点 但我们可能要和一堆路径合并, 怎么找到对于我手中已经有的一段路径找到另一条和它权值和拼起来最大的路径呢? 观察发现, 想一次函数, 把 \(l_1\) 看做 \(x\) , S看做k, 那就是找到一个一次函数 \(y = S_2 * x + V_2\) , 使 \(x = l_1\) 最大吗, 这个可以用李超线段树做 来源: https

hdu1754线段树

霸气de小男生 提交于 2020-02-28 17:01:27
#include "iostream" #include <cstdio> #define MAX 200005 #define lc(x) ((x)<<1) #define rc(x) (((x)<<1)+1) using namespace std; struct node { int l,r,m; }; int score[MAX]; node tree[MAX*3]; int N,M,p,q; char cmd[2]; int mmax(int x,int y) { return x>y? x:y; } int build(int l,int r,int i)//从下到上建树 { tree[i].l=l; tree[i].r=r; if(l==r) { tree[i].m=score[l]; return 0; } int mid=(l+r)>>1; build(l,mid,lc(i));//左儿子节点 build(mid+1,r,rc(i));//右儿子节点 tree[i].m=mmax(tree[lc(i)].m,tree[rc(i)].m);//父亲节点是其左右儿子中的大的 return 0; } int renew(int i ,int num, int newv)//从下向上更新树 { if(tree[i].r==num&&tree[i].l==num) tree