线段树

dfs序七个经典问题[转]

匿名 (未验证) 提交于 2019-12-03 00:43:02
dfs序七个经典问题  参考自:《数据结构漫谈》-许昊然 dfs序是树在dfs先序遍历时的序列,将树形结构转化成序列问题处理。 dfs有一个很好的性质:一棵子树所在的位置处于一个连续区间中。 ps:deep[x]为x的深度,l[x]为dfs序中x的位置,r[x]为dfs序中x子树的结束位置 1.点修改,子树和查询   在dfs序中,子树处于一个连续区间中。所以这题可以转化为:点修改,区间查询。用树状数组或线段树即可。 2.树链修改,单点查询   将一条树链x,y上的所有点的权值加v。这个问题可以等价为:   1).x到根节点的链上所有节点权值加v。   2).y到根节点的链上所有节点权值加v。   3).lca(x,y)到根节点的链上所有节点权值和减v。   4).fa(lca(x,y))到根节点的链上所有节点权值和减v。     上面四个操作可以归结为:节点x到根节点链上所有节点的权值加减v。修改节点x权值,当且仅当y是x的祖先节点时,x对y的值有贡献。   所以节点y的权值可以转化为节点y的子树节点贡献和。从贡献和的角度想:这就是点修改,区间和查询问题。   修改树链x,y等价于add(l[x],v),add(l[y],v),add(l[lca(x,y)],-v),add(l[fa(lca(x,y))],-v)。   查询:get_sum(r[x])-get_sum(l[x]

HDU 1166 【线段树入门】

匿名 (未验证) 提交于 2019-12-03 00:43:02
题目链接 HDU 1166 大概题意: 第一行一个整数T,表示有T组数据。 每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(1<=ai<=50)。 接下来每行有一条命令,命令有4种形式: (1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30) (2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30); (3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数; (4)End 表示结束,这条命令在每组数据最后出现; 每组数据最多有40000条命令 思路:类似于区间查询和区间修改等操作,操作数又较多的情况优先想线段树、树状数组等,因为线段树又是一颗平衡二叉树,所以可以用二叉树的构建方法,在这里用的是结构数组的表示方法。 结点 :T[a, b] (a, b 表示区间 [a, b] , 其中 b-a 为长度 len ) 线段树递归定义为: 若 len > 1 , 则 [a, (a+b)/2] 为 T 的左儿子, [(a+b)/2+1, b] 为 T 的右儿子。 若 len == 1, 则 T 为叶子节点。 复杂度: 线段树的深度不超过log2len, 线段树把区间上的任意一条线段都分成不超过 2log2len

CodeVs1082 线段树练习 3

匿名 (未验证) 提交于 2019-12-03 00:43:02
题解:一种特殊的树状数组写法 http://codevs.cn/problem/1082/ #include <bits/stdc++.h> #define ll long long const int MAXN=2e5+10; using namespace std; ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar(); return f*x; } ll a[MAXN],b[MAXN]; int get_id(int x){return x&(-x);} int n; void add1(int x,ll vul){ if(x<=0)return ; for(int i=x;i<=n;i+=get_id(i))a[i]+=vul; } void add2(int x,ll vul){ if(x<=0)return ; for(int i=x;i>0;i-=get_id(i))b[i]+=vul; } //ll ans; ll Sum1(int x){ if(x<=0)return 0; ll ans=0; for(int i=x;i>0;i-=get_id(i)

NOIP2017提高组Day2T3 列队 洛谷P3960 线段树

匿名 (未验证) 提交于 2019-12-03 00:39:02
原文链接https://www.cnblogs.com/zhouzhendong/p/9265380.html 题目传送门 - 洛谷P3960 题目传送门 - LOJ#2319 题目传送门 - Vijos P2033 题意   懒了,不概括了。       题解   一开始写了树状数组。   算法非常真,写完全部 WA,但是漏了一步,我快写吐了,于是弃疗之后从某度*了一份代码。   我来说说线段树的做法:   线段树动态开点,每行一个线段树,最后一列一个线段树。   线段树要支持找区间第 $k$ 大,这样方便找出指定位置。   注意一下我们会在行或者列线段树新增最多 $q$ 个元素,所以线段树处理的区间要开到 $\max(n,m)+q$ 。   然后就是纯模拟了。注意,无论是在行尾加入新元素,还是在最后一列尾加入新元素,我们都需要记录他们的值,用 $vector$ 存一下。   注意开 $long\ long$ 。 代码 #include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=300005,S=N*20; int n,m,q,Max,tot; int root[N],ls[S],rs[S],sum[S]; vector <LL> v[N]; int query(int rt

luogu3373线段树2..支持区间加值和乘值的线段树

匿名 (未验证) 提交于 2019-12-03 00:39:02
题面: luogu3373 和线段树1一样,这道题也要用Lazy标记思想,这个很好理解. 因为乘法优先,所以每次执行PushDown操作时要先算乘法. 再就是注意到对于乘法的标记,每次要更新的不仅是乘法的,还有加法的标记. 点我看代码 #include <cstdio> using namespace std; typedef long long ll; const int MAXN = 1e5 ; struct SegmentTree { ll mod, N, su[MAXN * 4 + 10 ], ad[MAXN * 4 + 10 ], ml[MAXN * 4 + 10 ]; void init() { for ( int i = 1 ; i <= N * 4 ; ++i) ml[i] = 1 ; } void PushDownPls(ll pos, ll numl, ll numr) { if (ad[pos] == 0 ) return ; ad[pos << 1 ] = (ad[pos << 1 ] + ad[pos]) % mod; ad[pos << 1 | 1 ] = (ad[pos << 1 | 1 ] + ad[pos]) % mod; su[pos << 1 ] = (su[pos << 1 ] + ad[pos] * numl % mod) % mod;

CodeForces 438D (线段树)

匿名 (未验证) 提交于 2019-12-03 00:36:02
题意: 三种操作: 1 l r : 1 l r : 求 ∑ r i = l a i ∑ i = l r a i 2 l r x : 2 l r x : 对所有 i ∈ [ l , r ] i ∈ [ l , r ] , a i = a i mod x a i = a i mod x 3 k x : 3 k x : a k = x a k = x 思路: 神一般的复杂度分析。。。。记录区间最大值,如果 x x 比区间最大值还大,显然不更新,否则暴力更新。因为如果是 y mod x y mod x 的话,如果 x > y 2 x > y 2 ,那么 y y 取模之后小于 y 2 y 2 ,如果 x < y 2 x < y 2 ,取模之后也 y 2 y 2 ,故每个数取模不会超过 log n log n 次。。。 #include<bits/stdc++.h> typedef long long ll; const int maxn = 1e5 + 10 ; using namespace std; ll sum [maxn * 4 ], val[maxn * 4 ]; void build( int o, int l, int r) { if (l == r) { cin >> sum [o]; val[o] = sum [o]; return ; } int mid = (l + r

【BZOJ】4574: [Zjoi2016]线段树-DP

匿名 (未验证) 提交于 2019-12-03 00:34:01
传送门: bzoj4574 转自 lych_cys 题意就是求每个数在所有方案中的最终值的和。显然一个数经过若干次变化一定会变成另外一个数,那么离散化后,令g[i][j]表示i这个数最终变成从小到大第j个的方案数。一个直观的思路是,我们枚举j,那么显然g[i][j]>0的i的范围是(l,r),其中a[l]和a[r]是第j大的数两侧分别第一个大于这个从小到大第j个数的数(由于是随机因此可以假定没有两个数相同)。此时, 如果令f[k][x][y]表示经过k轮后,恰好是[x,y]这个范围内的数都变成了从小到大第j个数的方案数。但是这样会存在问题,就是如果某一轮的操作跨过了l或r,就会造成[l,r]中某一些数>从小到大第j个数,这样再转移就会出错。 那么令f[k][x][y]表示经过k轮后,恰好[x,y]这个范围内的数都<=从小到大第j数的方案数,这样就可以转移了。显然f[k][x][y]必然由f[k][u(u #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define mod 1000000007 #define ll long long #define N 405 #define calc(x) ((x)*((x)+1)>>1) using namespace std; int n,

CodeForces - 834D(线段树优化dp)

匿名 (未验证) 提交于 2019-12-03 00:32:02
给你长度为N的一个序列,让你将其分成连续的k段,每段的价值为其中数字种类的个数,求最大价值总和。 首先能想到n^2复杂度的dp 设定dp[i][j]表示到位子i,分成j段的最大价值总和。 dp[i][j] =max( d p[i][j] , dp[k][j-1] +val(k+1,i) );k为这个数上一次出现的位置 可以用线段树加速转移。 考虑val(k+1,j). 我们遍历到第j个位子的时候,我们显然树上第k个位子表示的是 dp[k][j-1] + val(k+1,i) ,那么考虑第i个数,它会对区间 (pre[a[i]] ,i-1)区间内的树上的位子有所影响。 那么我们遍历到第i个位子的时候,将树上区间(pre[a[i]],i)的值都+1。 这里pre[a[i]]表示的是a[i]这个数上一次出现的位子。 #include<bits/stdc++.h> using namespace std; const int maxn = 35555; int a[maxn<<2],n,k; int delt[maxn<<2],pre[maxn],pos[maxn]; int dp[maxn][52]; void pushup(int rt) { a[rt]=max(a[rt*2],a[rt*2+1]); } void pushdown(int rt) { delt[rt<<1]+

spoj GSS 1-8的解析――线段树维护最大子段和一类问题的技巧

匿名 (未验证) 提交于 2019-12-03 00:30:01
题目spoj,但是太卡了,无奈之下水luogu. T1 : 给定一个序列,有M此操作,每次操作查询[l,r]的最大子段和,不可取空序列. 一道水题,我们可以得知,区间[l,r]的最大子段和只有三种可能:左半边最大子段和,右半边最大子段和,左半边一定包括最右端的最大子段和+右半边一定包括最左端的最大子段和. 于是我们线段树每个节点就维护四个信息:最大子段和sum,一定包括最左端最大子段和lsum,一定包括最右端最大子段和rsum,以及区间和ans. 那么我们就可以写出代码: #include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=100000; struct tree{ int l,r; int sum,lsum,rsum,ans; }tr[N*5]; int n,m,a[N+1]; void build(int L,int R,int k=1){ tr[k].l=L;tr[k].r=R; if (L==R){ tr[k].ans=tr[k].sum=tr[k].lsum=tr[k].rsum=a[L]; return; } int mid=L+R>>1; build(L,mid,k<<1); build(mid+1,R,k<<1|1); tr[k].ans=tr[k<<1].ans

敌兵布阵(线段树+单点更新)

匿名 (未验证) 提交于 2019-12-03 00:22:01
这道题算是线段树的入门题了,线段树理解的话其实是挺好理解的,但写的话就感觉挺不好写的,毕竟有好几十行代码,讲解的话看下别人的博客,他们讲的我觉得很清楚了,然后如果觉得我的代码风格跟你差不多的,能接受我的这种写法的话,有什么不懂得可以问我。 AC代码: #include <iostream> #include <cstdio> #include <cstring> #include <string> #define lson l , mid , o << 1 #define rson mid + 1 , r , o << 1 | 1 #define maxn 50005 using namespace std ; int sum [ maxn << 2 ]; int T , n ; string str ; void Pushup ( int o ){ sum [ o ] = sum [ o << 1 ] + sum [ o << 1 | 1 ]; // 向上更新维护一个sum值 } void Build ( int l , int r , int o ){ if ( l == r ){ scanf ( "%d" ,& sum [ o ]); return ; } int mid = ( l + r ) >> 1 ; Build ( lson ); Build ( rson );