一片森林之线段树

版权声明:未经允许禁止转载。转载请联系我WX:yuyi5453。并且注明出处 https://blog.csdn.net/weixin_40532377/article/details/82784855

先上几道线段树的题目,AC代码我放在最后,大家可以先直接看一眼题目,自己想想思路

https://nanti.jisuanke.com/t/237

http://acm.hdu.edu.cn/showproblem.php?pid=1166

http://acm.hdu.edu.cn/showproblem.php?pid=1394

首先,线段树是什么?

神奇海螺回答:线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

不明白没关系,先把上面的三个题目看一下,找一下他们的共同点。

是不是它们都有类似这样的特点:有一个从1到n的区间(例如地主记账从1到n编号,军队按照河岸驻扎从1到n编号)

所以知道为啥叫  “线段树” 了8;

然后让你求修改某点的值,或者求某个区间的属性,而且是多次求。

可以怎么求?直接for循环不就完了!?求一次for一次,时间复杂度呢,O(N),超时没商量;

这时候线段树的优势就就体现出来了。

停!!       线段树还没弄明白是啥呢,怎么就体现优势了

那线段树到底是个啥树呢?有请神奇海螺:

首先,一棵树每个节点必须要有三个或以上的属性,其中两个是左端点、右端点,另一个就是你需要用的值啦,比如求的和,最小值等等等等。

如下图:给你一个1~13区间,【i,j】分别代表左右端点,根节点就是【1,13】,然后建左子树、右子树。

最终在叶子处分成单个点。

建树代码:(你可以使用指针,也可以用数组模拟,注意数组模拟的时候把数组开到 数据个数的四倍 才不会溢出)

void build_tree(int l,int r,int k){		//建树是所有线段树的第一步 
	tree[k].l=l;
	tree[k].r=r;
	if(l==r) return ;
	int mid=(l+r)/2;
	build_tree(l,mid,k*2);
	build_tree(mid+1,r,k*2+1);
}

好了,建树过程就完了,然后就怎么去使用的问题了。

这里就说一下最常用的几个使用,至于其他的还需要在题目中补充啦。

First:修改某个点的值:

void update(int i,int t,int num){
	if(tree[t].l==i&&tree[t].r==i) {    //找到这个节点后进行修改
		tree[t].sum+=num;
		return;
	}
	int mid=(tree[t].l+tree[t].r)/2;
	if (mid>=i) update(i,2*t,num);    //向左
	else update(i,2*t+1,num);        //向右    
	
	tree[t].sum=tree[2*t].sum+tree[2*t+1].sum;        //修改完叶子的要更新父节点的
}

Second:

求某个区间的XXX:

int ss;
void search(int l,int r,int t){
	if(tree[t].l>=l&&tree[t].r<=r){
		ss+=tree[t].sum;
		return ;	
	}
	int mid=(tree[t].l+tree[t].r)>>1;
    //仔细想一下下面这几行
	if(r<=mid) search(l,r,t*2);
	else if(l>mid) search(l,r,t*2+1);
	else{
		search(l,mid,t*2);
		search(mid+1,r,t*2+1);
	}
}

至于其他的想起来再更~~

下面是三道题的AC代码(可以参考下)

第一题:

#include<bits/stdc++.h>
using namespace std;
const int maxn= 100005;
struct node{
	int l;
	int r;
	int minv,minp;
}tree[maxn*4];
void build_tree(int l,int r,int k){		//建树是所有线段树的第一步 
	tree[k].l=l;
	tree[k].r=r;
	if(l==r) return ;
	int mid=(l+r)/2;
	build_tree(l,mid,k*2);
	build_tree(mid+1,r,k*2+1);
}

void update(int k,int i,int data){
	int l=tree[k].l,r=tree[k].r;
	if(i==l&&i==r){
		tree[k].minv=data;
		return;
	}
	int mid=(l+r)/2;
	if(i<=mid) update(2*k,i,data);
	else update(2*k+1,i,data);
	
	tree[k].minv=min(tree[2*k].minv,tree[2*k+1].minv);
//	tree[k].minp=(tree[2*k].minv<tree[2*k+1].minv?tree[2*k].minp:tree[2*k+1].minp);
}
int ans,p;
void search(int k,int l,int r){
	int left=tree[k].l,right=tree[k].r;
	if(l==left&&r==right){
		ans=min(ans,tree[k].minv);
		return;
	}
	int mid=(left+right)/2;
	if(r<=mid) search(k*2,l,r);
	else if(l>mid) search(k*2+1,l,r);
	else{
		search(k*2,l,mid);
		search(k*2+1,mid+1,r);
	}
}
int main()
{
	memset(tree,0,sizeof(tree));
	int m,n,temp;
	while(cin>>m>>n){
		build_tree(1,m,1);
		for(int i=1;i<=m;i++){
			cin>>temp;
			update(1,i,temp);
		}
		int l,r; 
		while(n--){
			ans=1e8;
			cin>>l>>r;
			search(1,l,r);
			printf("%d%c",ans,n==0?'\n':' ');
		}
	}
	
	return 0;
 } 

第二题:

#include<iostream>
#include<cstring>
using namespace std;
struct node{
	int l,r,sum;
};
const int maxn=50005;
node tree[maxn*4];
void buile(int l,int r,int t){
	tree[t].l=l;
	tree[t].r=r;
	tree[t].sum=0;
	if(r==l) return;
	int mid=(l+r)>>1;
	buile(l,mid,t*2);
	buile(mid+1,r,t*2+1);
}
void update(int i,int t,int num){
	if(tree[t].l==i&&tree[t].r==i) {
		tree[t].sum+=num;
		return;
	}
	int mid=(tree[t].l+tree[t].r)/2;
	if (mid>=i) update(i,2*t,num);
	else update(i,2*t+1,num);
	
	tree[t].sum=tree[2*t].sum+tree[2*t+1].sum;
}
int ss;
void search(int l,int r,int t){
	if(tree[t].l>=l&&tree[t].r<=r){
		ss+=tree[t].sum;
		return ;	
	}
	int mid=(tree[t].l+tree[t].r)>>1;
	if(r<=mid) search(l,r,t*2);
	else if(l>mid) search(l,r,t*2+1);
	else{
		search(l,mid,t*2);
		search(mid+1,r,t*2+1);
	}
}
int main()
{
	int t,n,temp,a,b;
	string st;
	cin>>t;
	for (int i=1;i<=t;i++){
		printf("Case %d:\n",i);
		scanf("%d",&n);
		buile(1,n,1);
		for (int i=1;i<=n;i++){
			scanf("%d",&temp);
			update(i,1,temp);
		}
		while(cin>>st&&st[0]!='E'){
			ss=0;
			scanf("%d%d",&a,&b);
			if(st[0]=='Q'){
				search(a,b,1);
				printf("%d\n",ss);
			}
			else if(st[0]=='A'){
				update(a,1,b);
			}
			else if(st[0]=='S'){
				update(a,1,-b);
			}
		}
	}
	return 0;
 } 

第三题:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=5010;
int a[maxn];
int n,ans;
struct node{
   int left,right,sum;
}t[maxn<<2];
void build(int k,int l,int r)
{
    t[k].left=l;t[k].right=r;
    t[k].sum=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
void update(int k,int val)
{
    int l=t[k].left,r=t[k].right;
    if(l==val && val==r){
        t[k].sum++;
        return;
    }
    int mid=(l+r)>>1;
    if(val<=mid) update(k<<1,val);
    else update(k<<1|1,val);
    t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}
void query(int k,int l,int r)
{
    if(l==t[k].left && r==t[k].right)
    {
        ans+=t[k].sum;
        return;
    }
    int mid=(t[k].left+t[k].right)>>1;
    if(r<=mid) query(k<<1,l,r);
    else if(l>mid) query(k<<1|1,l,r);
    else{
        query(k<<1,l,mid);
        query(k<<1|1,mid+1,r);
    }
}
int main()
{
    while(scanf("%d",&n) != EOF)
    {
        build(1,0,n);//虽然只有到n-1,但是由于后面用到a[i]+1,为防止溢出,树区间要到n
        ans=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            query(1,a[i],n-1);//计算比a[i]大的数的个数。注:这些数是包含于插入a[i]前的
            update(1,a[i]);//此时将a[i]插入
        }
        int temp=ans;
        for(int i=0;i<n-2;i++)
        {
            ans=ans-a[i]+(n-a[i]-1);
            temp=min(temp,ans);
        }
        printf("%d\n",temp);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40532377/article/details/82784855