HH神的线段树出神入化,所以跟着HH学习线段树。
风格:
maxn是题目给的最大区间,而节点数要开4倍,确切的说……
lson和rson辨别表示结点的左孩子和右孩子。
PushUp(int rt)是把当前结点的信息更新到父节点
PushDown(int rt)是把当前结点的信息更新给孩子结点。
rt表示当前子树的根(root),也就是当前所在的结点。
思想:
对于每个非叶节点所标示的结点 [a,b],其做孩子表示的区间是[a,(a+b)/2],其右孩子表示[(a+b)/2,b].
构造:
离散化和线段树:
题目:x轴上有若干个线段,求线段覆盖的总长度。
普通解法:设置坐标范围[min,max],初始化为0,然后每一段分别染色为1,最后统计1的个数,适用于线段数目少,区间范围小。
离散化的解法:离散化就是一一映射的关系,即将一个大坐标和小坐标进行一一映射,适用于线段数目少,区间范围大。
例如:[10000,22000],[30300,55000],[44000,60000],[55000,60000].
第一步:排序 10000 22000 30300 44000 55000 60000
第二部:编号 1 2 3 4 5 6
第三部:用编号来代替原数,即小数代大数 。
[10000,22000]~[1,2]
[30300,55000]~[3,5]
[44000,60000]~[4,6]
[55000,60000]~[5,6]
然后再用小数进行普通解法的步骤,最后代换回去。
线段树的解法:线段树通过建立线段,将原来染色O(n)的复杂度减小到 log(n),适用于线段数目多,区间范围小的情况。
离散化的线段树:适用于线段数目多,区间范围大的情况。
构造:
动态数据结构:
struct node{
node* left;
node* right;
……
}
静态全局数组模拟(完全二叉树):
struct node{
int left;
int right;
……
}Tree[MAXN]
例如:
线段树与点树:
线段树的每一个结点表示一个点,成为点树,比如说用于求第k小数的线段树。
点树结构体:
struct node{
int l, r;
int c;//用于存放次结点的值,默认为0
}T[3*MAXN];
创建:
创建顺序为先序遍历,即先构造根节点,再构造左孩子,再构造右孩子。
- voidconstruct(intl,intr,intk){
- T[k].l=l;
- T[k].r=r;
- T[k].c=0;
-
if(l==r)return;
-
intm=(l+r)>>1;
- construct(l,m,k<<1);
- construct(m+1,r,(k<<1)+1);
-
return;
- }
void construct(int l, int r, int k){
T[k].l = l;
T[k].r = r;
T[k].c = 0;
if(l == r) return ;
int m = (l + r) >> 1;
construct(l, m, k << 1);
construct(m + 1, r, (k << 1) + 1);
return ;
}
[A,B,C]:A表示左值,B表示右值,C表示在静态数组中的位置,由此可知,n个点的话大约共有2*n个结点,因此开3*n的结构体一定是够的。
更新值:
- voidinsert(intd,intk){
-
-
if(T[k].l==T[k].r&&d==T[k].l){
- T[k].c+=1;
-
return;
- }
-
intm=(T[k].l+T[k].r)>>1;
-
if(d<=m)insert(d,k<<1);
-
elseinsert(d,(k<<1)+1);
-
- T[k].c=T[k<<1].c+T[(k<<1)+1].c;
- }
void insert(int d, int k){
//如果找到了就c值+1返回。
if(T[k].l == T[k].r && d == T[k].l){
T[k].c += 1;
return ;
}
int m = (T[k].l + T[k].r) >> 1;
if(d <= m) insert(d, k << 1);
else insert(d, (k << 1) + 1);
//更新每一个c,向上更新
T[k].c = T[k << 1].c + T[(k << 1) + 1].c;
}
查找值:
-
voidsearch(intd,intk,int&ans)
- {
-
if(T[k].l==T[k].r){
- ans=T[k].l;
- ans=T[k].l;
- }
-
intm=(T[k].l+T[k].r)>>1;
-
-
if(d>T[(k<<1)].c)search(d-T[k<<1].c,(k<<1)+1,ans);
-
elsesearch(d,k<<1,ans);
- }
//k表示树根,d表示要查找的值
void search(int d, int k, int& ans)
{
if(T[k].l == T[k].r){
ans = T[k].l;
ans = T[k].l;
}
int m = (T[k].l + T[k].r) >> 1;
//不懂
if(d > T[(k << 1)].c) search(d - T[k << 1].c, (k << 1) + 1, ans);
else search(d, k << 1, ans);
}
search函数的用法不太懂。
例题解:
(待更新)
四类题型:
1.单点更新 只更新叶子结点,然后把信息用PushUp(int r)这个函数更新上来。
hdu1166:敌兵布阵
线段树功能:update:单点替换 query:区间最值
poj2828
树状数组:
- #include<iostream>
-
#include<cstdio>
-
#include<string>
-
#include<cstring>
-
usingnamespacestd;
-
typedefpair<int,int>PII;
-
constintmaxn=200000;
-
intC[maxn+100];
-
intB[maxn+100];
-
intn;
- PIIarr[maxn+100];
-
intlowbit(intk){returnk&(-k);}
-
voidinit(){
-
for(inti=1;i<=n;i++)C[i]=lowbit(i);
- memset(B,-1,n+10);
- }
-
voidupdate(inti){
-
while(i<=n){
- C[i]--;
- i+=lowbit(i);
- }
- }
-
intquery(inti){
-
intret=0;
-
while(i>0){
- ret+=C[i];
- i-=lowbit(i);
- }
-
returnret;
- }
-
voiddebug(){
-
for(inti=1;i<=n;i++)cout<<i<<""<<query(i)<<endl;
- }
-
voidfun(inta,intv){
-
intl=1,r=n;
-
while(l<r){
-
intm=(l+r)>>1;
-
if(query(m)>=a)r=m;
-
elsel=m+1;
- }
-
- update(l);
-
-
- B[l]=v;
-
- }
-
intmain(){
-
while(~scanf("%d",&n)){
- init();
-
inta,b;
-
for(inti=1;i<=n;i++){
-
scanf("%d%d",&a,&b);
- a++;
- arr[i].first=a;
- arr[i].second=b;
- }
-
for(inti=n;i>0;i--)fun(arr[i].first,arr[i].second);
-
-
-
for(inti=1;i<=n;i++){
-
i==1?printf("%d",B[i]):printf("%d",B[i]);
-
-
- }
-
puts("");
- }
-
return0;
- }
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
typedef pair<int, int> PII;
const int maxn = 200000;
int C[maxn + 100];
int B[maxn + 100];
int n;
PII arr[maxn + 100];
int lowbit(int k) { return k & (-k); }
void init() {
for(int i = 1; i <= n; i++) C[i] = lowbit(i);
memset(B, -1, n + 10);
}
void update(int i) {
while(i <= n) {
C[i]--;
i += lowbit(i);
}
}
int query(int i) {
int ret = 0;
while(i > 0) {
ret += C[i];
i -= lowbit(i);
}
return ret;
}
void debug() {
for(int i = 1; i <= n; i++) cout << i << " " << query(i) << endl;
}
void fun(int a, int v) {
int l = 1, r = n;
while(l < r) {
int m = (l + r) >> 1;
if(query(m) >= a) r = m;
else l = m + 1;
}
//cout << "here " << l << endl;
update(l);
//cout << "here2 " << endl;
//debug();
B[l] = v;
//return l;
}
int main() {
while(~scanf("%d", &n)) {
init();
int a, b;
for(int i = 1; i <= n; i++) {
scanf("%d%d", &a, &b);
a++;
arr[i].first = a;
arr[i].second = b;
}
for(int i = n; i > 0; i--) fun(arr[i].first, arr[i].second);
//debug2();
//bool flag = false;
for(int i = 1; i <= n; i++) {
i == 1 ? printf("%d", B[i]) : printf(" %d", B[i]);
//if(B[i] != -1 && !flag) { printf("%d", B[i]); flag = true; }
//else if(B[i] != -1) printf(" %d", B[i]);
}
puts("");
}
return 0;
}
poj-3468
- #include<cstdio>
-
#include<cstring>
-
#include<iostream>
-
usingnamespacestd;
-
#definelsonl,m,rt<<1
-
#definersonm+1,r,rt<<1|1
-
typedeflonglongLL;
-
constintmaxn=111111;
- LLcol[maxn<<2];
- LLsum[maxn<<2];
-
voidPushUp(LLrt){
- sum[rt]=sum[rt<<1]+sum[rt<<1|1];
- }
-
-
-
-
voidPushDown(LLrt,LLm){
-
if(col[rt]){
-
- col[rt<<1]+=col[rt];
- col[rt<<1|1]+=col[rt];
- sum[rt<<1]+=col[rt]*(m-(m>>1));
- sum[rt<<1|1]+=col[rt]*(m>>1);
- col[rt]=0;
- }
- }
-
voidbuild(LLl,LLr,LLrt){
- col[rt]=0;
-
-
if(l==r){
-
scanf("%I64d",&sum[rt]);
-
-
return;
- }
-
intm=(l+r)>>1;
- build(lson);
- build(rson);
- PushUp(rt);
- }
- LLquery(LLL,LLR,LLl,LLr,LLrt){
- LLret=0;
-
if(L<=l&&r<=R){
-
-
returnsum[rt];
- }
- PushDown(rt,r-l+1);
-
intm=(l+r)>>1;
-
if(L<=m)ret+=query(L,R,lson);
-
if(R>m)ret+=query(L,R,rson);
-
returnret;
- }
-
voidupdate(LLL,LLR,LLc,LLl,LLr,LLrt){
-
if(L<=l&&r<=R){
- sum[rt]+=c*(r-l+1);
-
col[rt]+=c;
-
return;
- }
- PushDown(rt,r-l+1);
-
intm=(l+r)>>1;
-
if(L<=m)update(L,R,c,lson);
-
if(R>m)update(L,R,c,rson);
- PushUp(rt);
- }
-
voiddebug(intn){
-
for(inti=1;i<=(n*3);i++){
-
cout<<i<<"";
- }
- cout<<endl;
-
for(inti=1;i<=(n*3);i++){
-
cout<<col[i]<<"";
- }
- cout<<endl<<endl;
-
for(inti=1;i<=(n*3);i++){
-
cout<<i<<"";
- }
- cout<<endl;
-
for(inti=1;i<=(n*3);i++){
-
cout<<sum[i]<<"";
- }
- cout<<endl;
- }
-
intmain(){
- LLN,Q;
-
while(~scanf("%I64d%I64d",&N,&Q)){
-
-
memset(sum,0,sizeof(sum));
-
memset(col,0,sizeof(col));
- build(1,N,1);
-
-
for(inti=0;i<Q;i++){
-
charch[3];
- LLa,b,c;
-
scanf("%s",ch);
-
if(ch[0]=='Q'){
-
scanf("%I64d%I64d",&a,&b);
-
printf("%I64d\n",query(a,b,1,N,1));
- }
-
else{
-
scanf("%I64d%I64d%I64d",&a,&b,&c);
- update(a,b,c,1,N,1);
- }
-
- }
- }
-
return0;
- }
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
typedef long long LL;
const int maxn = 111111;
LL col[maxn<<2];
LL sum[maxn<<2];
void PushUp(LL rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
//pushdown的作用是如果此点可以更新。
//也就是更新到下一层
//如果是底层,那么是不用pushdown的。
void PushDown(LL rt, LL m) {
if(col[rt]) {
//col[rt<<1] = col[rt<<1|1] = col[rt];
col[rt<<1] += col[rt];
col[rt<<1|1] += col[rt];
sum[rt<<1] += col[rt] * (m - (m>>1));
sum[rt<<1|1] += col[rt] * (m>>1);
col[rt] = 0;
}
}
void build(LL l, LL r, LL rt) {
col[rt] = 0;
//cout << l << " " << r << endl;
if(l == r) {
scanf("%I64d", &sum[rt]);
//cout << rt << " " << sum[rt] << endl;
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
LL query(LL L, LL R, LL l, LL r, LL rt) {
LL ret = 0;
if(L <= l && r <= R) {
//if(col[rt]) return sum[rt] + (r - l + 1) * col[rt];
return sum[rt];
}
PushDown(rt, r - l + 1);
int m = (l + r) >> 1;
if(L <= m) ret += query(L, R, lson);
if(R > m) ret += query(L, R, rson);
return ret;
}
void update(LL L, LL R, LL c, LL l, LL r, LL rt) {
if(L <= l && r <= R) {
sum[rt] += c * (r - l + 1);
col[rt] += c;//子节点没有更新
return ;
}
PushDown(rt, r - l + 1);
int m = (l + r) >> 1;
if(L <= m) update(L, R, c, lson);
if(R > m) update(L, R, c, rson);
PushUp(rt);
}
void debug(int n) {
for(int i = 1; i <= (n*3); i++) {
cout << i << " ";
}
cout << endl;
for(int i = 1; i <= (n*3); i++) {
cout << col[i] << " ";
}
cout << endl << endl;
for(int i = 1; i <= (n*3); i++) {
cout << i << " ";
}
cout << endl;
for(int i = 1; i <= (n*3); i++) {
cout << sum[i] << " ";
}
cout << endl;
}
int main() {
LL N, Q;
while(~scanf("%I64d%I64d", &N, &Q)) {
//cout << "N = " << N << endl;
memset(sum, 0, sizeof(sum));
memset(col, 0, sizeof(col));
build(1, N, 1);
//debug(N);
for(int i = 0; i < Q; i++) {
char ch[3];
LL a, b, c;
scanf("%s", ch);
if(ch[0] == 'Q') {
scanf("%I64d%I64d", &a, &b);
printf("%I64d\n", query(a, b, 1, N, 1));
}
else {
scanf("%I64d%I64d%I64d", &a, &b, &c);
update(a, b, c, 1, N, 1);
}
//debug(N);
}
}
return 0;
}
分享到:
相关推荐
浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计...
一种简单的线段树的实现 ,基础功能比较完善
一个讲述线段树的好资料,这里主要是程序部分,希望对广大成员能够有所帮助
线段树、线段树啊、线段树,线段树啊、线段树
手打了一份线段树代码,用于c++编程, 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 使用线段树可以快速的查找某一个节点在若干条线段中出现的...
线段树ppt,线段树ppt,线段树ppt,树ppt,线段树ppt,线段树ppt,
线段树区间更新代码线段树区间更新代码线段树区间更新代码线段树区间更新代码
一个线段是对应于一个区间的,因此线段树也可以叫做区间树。 线段树是一棵二叉树,树中的每一个结点表示了一个区间[a,b]。每一个叶子节点表示了一个单位区间。对于每一个非叶结点所表示的结点[a,b],其左儿子表示的...
权值线段树和主席树入门PPT,权值线段树,顾名思义就是记录权值的线段树,普通的线段树直接以坐标为l,r建树,而权值线段树是以大小来建树,树上寸的信息是该权值的数量,而通过建树时二分从小到大的性质,可以用这...
讲解线段树基本应用,适合初学者下载使用!
ACM学习中 涉及到线段树的代码分析模板
著名的线段树六题 囊括线段树整个知识点, 十分实用 当年凭借此六题, NOIP+NOI秒杀线段树题
线段树学习ppt
本资源是对线段树操作比较完整的操作,包括线段树的动态插入,动态删除和维护,可以查询区段的最大值,最小值,完成线段树的基本操作。
线段树PPT,所有常规用法线段树PPT,所有常规用法线段树PPT,所有常规用法线段树PPT,所有常规用法
线段树,针对一组数的的统计十分方便。 线段树,针对一组数的的统计十分方便。 线段树,针对一组数的的统计十分方便。 线段树,针对一组数的的统计十分方便。 线段树,针对一组数的的统计十分方便。
线段树完全版,涉及到线段树的所有用法。 包括单点更新(增减,替换),区间求和,区间最值。 区间求最大值的位置。 成段更新(延迟标记,增减)。 离散化 扫描线
go语言实现的线段树源码, 可以直接运行, 代码简洁清晰, 快去下载吧
线段树点更新
线段树(一)