线段树

P3373 【模板】线段树 2(题解)

柔情痞子 提交于 2020-01-29 20:14:02
线段树解法 好丢脸,这个题做了一下午,调试了三个多小时...... 先讲讲解题思路 既然这里是线段树,就要用到lazy—tag。又有加法又有乘法的话,就要用到两个lazy-tag,分别用数组jia[]和chng[]表示。线段树用数组t[]存。 我们让lazy-tag还原数值时,先乘chng[],再加jia[](人为规定,这样好算) 怎么维护lazy-tag? 加法 void add( k, l, r, x, y, delta) { 函数的作用是在编号为k,区间是[l,r]的线段树里,给区间[x,y]里的每一个数加上delta。 如果当前区间[l,r]和目标区间[x,y]完全重合,就要在当前这颗编号为k的树上标记。 首先jia[k]要加上delta,表示当前区间[l,r](即[x,y])内的每一个数都加了delta; 然后要修改t[k]的值,也就是加上区间内增加的总数,即t[k]+=delta*(r-l+1); return。 如果当前区间不与目标区间完全重合,就要对子树操作。 首先,标记下传,用pushdown()函数将树k的标记全数下传给两个儿子k*2和k*2+1; 然后,先取mid=(l+r)>>1,判断一下目标区间是在当前区间的左子树区间、还是右子树区间、还是左右都有; (如果y<=mid,那么目标区间一定只在左子树里;如果x>=mid+1,那么目标区间一定只在右子树里

线段树基础题

混江龙づ霸主 提交于 2020-01-29 18:16:06
一:HDU1754 #include<iostream> #include<algorithm> using namespace std; const int maxn=2e+5; int n,m,a[maxn]; struct tree{ int l,r,v; }trees[maxn<<2]; void buildtree(int s,int l,int r) { trees[s].l=l,trees[s].r=r; if(l==r) { trees[s].v=a[l]; return ; } int mid=l+r>>1; buildtree(s<<1,l,mid); buildtree(s<<1|1,mid+1,r); trees[s].v=max(trees[s<<1].v,trees[s<<1|1].v); } void update_point(int s,int a,int m) { if(trees[s].l==a&&trees[s].r==a) { trees[s].v=m; return ; } int mid=trees[s].l+trees[s].r>>1; if(a<=mid) update_point(s<<1,a,m); if(a>mid) update_point(s<<1|1,a,m); trees[s].v=max(trees[s<<1].v

树状数组与线段树(二)

流过昼夜 提交于 2020-01-28 16:16:03
树状数组 1.小朋友排队 n 个小朋友站成一排。 现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。 每个小朋友都有一个不高兴的程度。 开始的时候,所有小朋友的不高兴程度都是 0 。 如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1 ,如果第二次要求他交换,则他的不高兴程度增加 2 (即不高兴程度为 3 ),依次类推。当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k 。 请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。 如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。 输入格式 输入的第一行包含一个整数 n ,表示小朋友的个数。 第二行包含 n n 个整数 H 1 , H 2 , … , H n ,分别表示每个小朋友的身高。 输出格式 输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。 数据范围 1 ≤ n ≤ 100000 0 ≤ H i ≤ 1000000 输入样例: 3 3 2 1 输出样例: 9 样例解释 首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。 解题思路:解题关键就是找到最少的交换次数,我们可能会想到冒泡排序法,他就是通过不断的交换排序来实现的,因此我们可以大胆的假设一些规律,最少的交换次数==逆序对的个数。

HDU 6521 Party(线段树)

爱⌒轻易说出口 提交于 2020-01-28 15:36:29
   题目意思:有n个人,一开始相互不认识。他们要去参加party,每次参加的人是编号在区间[l,r]内的人。参加完一次party之后,这区间内的人就会相互认识。每次会有有多少对人会新认识。那么用f[i] = j 表示 j - i的人都互相认识了,初始化f[i] = i,那么每次更新l,r话只需要找到是否存在f[l - r] > l并进行更新。 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const int N = 5e5 + 10; 8 9 int n, m; 10 int val[N << 2], num[N << 2]; 11 12 int read() 13 { 14 int res = 0, f = 1; 15 char c = getchar(); 16 while(c < '0' || c > '9') 17 { 18 if(c == -1) 19 { 20 f = -1; 21 } 22 c = getchar(); 23 } 24 while(c >= '0' && c <= '9') 25 { 26 res = res * 10 + c - '0'; 27 c = getchar(); 28 } 29 return f * res; 30 }

基于线段树的RMQ的实现(挑战程序设计模板)

蹲街弑〆低调 提交于 2020-01-28 13:34:55
#include<iostream> using namespace std; #include<queue> #include<algorithm> typedef long long ll; const int MAX_N=1<<17;//开辟的数组空间 int n,dat[2*MAX_N-1];//存储线段树的全局数组 void init(int n_){//初始化操作 n=1; while(n<n_) n*=2; for(int i=0;i<2*n-1;i++) dat[i]=INT_MAX; } //更新--->把第k个值更新为a; void update(int k,int a){ k+=n-1; dat[k]=a; while(k>0){ k=(k-1)/2; dat[k]=min(dat[k*2+1],dat[k*2+2]); } } //求最值:---->求[a,b)的最小值(通常取为左闭右开) //外部调用时query(a,a,0,0,n) int query(int a,int b,int k,int l,int r){ if(r<=a||b<=l) return INT_MAX; if(a<=l&&r<=b) return dat[k]; else{ int v1=query(a,b,2*k+1,l,(l+r)/2); int v2=query(a,b,2

【题解】 AtCoder ARC 076 F - Exhausted? (霍尔定理+线段树)

元气小坏坏 提交于 2020-01-28 05:01:45
题面 题目大意: 给你 \(m\) 张椅子,排成一行,告诉你 \(n\) 个人,每个人可以坐的座位为 \([1,l]\bigcup[r,m]\) ,为了让所有人坐下,问至少还要加多少张椅子。 Solution: 为什么加椅子?我们可以在最左边或最右边一直加直到人人都有座位。 首先这道题目抽象成二分图很简单,然后我们可以只要求解出人与座位的最大匹配是多少,总人数减去即可,但跑二分图最大匹配显然会超时,我们就可以往霍尔定理方面想。 然后你还需要知道一个霍尔定理推论:假设某个人的集合为 \(X\) ,这个集合所对应的椅子的集合为 \(Y\) ,如果 \(|X|\leq|Y|\) ,则具有完美匹配,如果 \(|X|\geq|Y|\) ,则 \(X\) 至少要删去 \(|X|-|Y|\) 个元素,才能有完备匹配,我们定义 \(\Gamma(X)=|X|-|Y|\) 。 在这题里,这个就是至少需要添加的椅子数目,所以我们要找出最大的 \(\Gamma(X)\) 接下来我们就来分析怎么找出最大的 \(\Gamma(X)\) ,因为 \(X\) 不具有任何性质,不好下手,我们考虑 \(Y\) 有啥特点,他一定是 \([1,l]\bigcup[r,m]\) ,然后我们可以通过这个 \(Y\) 确定 \(|X|\) ,所以会有一下做法 法一:暴力枚举 暴力枚举 \(l,r\) ,椅子的个数为 \(l

(线段树 && 字符串的处理)codeforces -- 570C

会有一股神秘感。 提交于 2020-01-28 04:30:38
链接: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=87813#problem/J Description Daniel has a string s , consisting of lowercase English letters and period signs (characters ' .'). Let's define the operation of replacement as the following sequence of steps: find a substring " .." (two consecutive periods) in string s , of all occurrences of the substring let's choose the first one, and replace this substring with string " .". In other words, during the replacement operation, the first two consecutive periods are replaced by one. If string s contains no two consecutive periods, then

POJ 3667 Hotel(线段树+区间合并)

筅森魡賤 提交于 2020-01-28 04:29:26
http://poj.org/problem?id=3667 题意: 有N个房间,M次操作。有两种操作(1)"1a",表示找到连续的长度为a的空房间,如果有多解,优先左边的,即表示入住。(2)"2 b len",把起点为b长度的len的房间清空,即退房。 思路: 区间合并的线段树题。 其实如果单点更新和区间更新做得多了的话,区间合并也就不难了。 对于这道题,我们用线段树维护三个值,从左端开始的连续空房间数,从右边开始的连续空房间数,以及整个区间内最大的连续空房间数。 1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<stack> 7 #include<queue> 8 #include<cmath> 9 #include<map> 10 #include<set> 11 using namespace std; 12 typedef long long ll; 13 const int INF = 0x3f3f3f3f; 14 const int maxn=50000+5; 15 16 int n, m; 17 18 struct node //维护从左端开始的最大连续空房间,从右端开始的最大连续空房间

HDU1394线段树

僤鯓⒐⒋嵵緔 提交于 2020-01-27 01:03:49
题目链接 hdu1394 题意:给n个数,可以将前m个数放到最后,一共有n种情况,要求这n种情况中,逆序对个数最少数多少。 思路:在每次插入前,使用线段树求出大于当前点的个数。最后在求出每次移动一个数到最后后逆序对打个数。假如原本序列的逆序对个数为m,第一个数为x,那么如果把0移到最后,那么逆序对数目则为m+n-1-2*x code: # include <bits/stdc++.h> using namespace std ; # define mid ((l + r) >> 1) # define lson rt << 1, l, mid # define rson rt << 1 | 1, mid + 1, r const int maxn = 5500 ; int tree [ maxn << 2 ] ; int v [ maxn ] ; int query ( int rt , int l , int r , int L , int R ) { if ( l == L && R == r ) return tree [ rt ] ; if ( R <= mid ) return query ( lson , L , R ) ; if ( L > mid ) return query ( rson , L , R ) ; return query ( lson , L ,

zkw线段树

眉间皱痕 提交于 2020-01-26 23:59:31
zkw线段树是对普通线段树的常熟优化版本,$≈$树状数组的常数。 同时普通线段树是近似完全二叉树,而zkw线段树是满二叉树,且普通线段树自上而下访问,zkw线段树先找到叶子节点,自下而上进行访问。 那么易得建树 void Build () { for (M = 1; M <= N + 1; M <<= 1); for (int i = M + 1; i <= M + N; i ++) Tree[i].val = A[i - M]; for (int i = M - 1; i >= 1; i --) Tree[i].val = Tree[i << 1].val + Tree[i << 1 | 1].val; } 对于区间修改或查询,我们假定此时区间为$[L, R]$,那么令$s = L - 1$,$t = R + 1$(需找到叶子节点编号),同时向上跳,且满足:若$s$所在为左子节点,则修改其右子节点,反之不进行操作;同理若t所在为右子节点,则修改其左子节点,反之不进行操作,并且结束条件为s与t成为兄弟。 可以发现,依据这样的规则$s$和$t$会遍历普通线段树上所有需进行修改的节点,并且冗余的节点是不会被修改访问到的。 那么先进行较简单的区间加减,及区间求和操作。 对于无法下传的懒标记,令其为永久性标记即可。 void Modify (int L, int R, LL k) { LL