校门外的树——线段树——大力出奇迹

  这题其实只需要暴力求解即可,也只要O(nm)。但是用一下线段树,大力出奇迹不好吗,也可以随便练练线段树。

  线段树值得注意的是——空间要开4倍大,虽然实际空间只有2倍,但是它并不是一棵完全二叉树。

  还有一点就是坐标轴是从0开始的,而我从1开始,导致我调试了好久才发现。

#include
#include
#include
#include
using namespace std;
const int maxn=10010;
int n,m;
int getin(){
    int num=0,t=1;
    char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();}
    while(c<='9'&&c>='0'){num=num*10+c-'0';c=getchar();}
    return num*t;
}
struct Yzyet{int mark,num;}tree[4*maxn];//线段树一般开4倍的空间 
//mark即懒标记,num保存当前树的个数 
void build(int root,int l,int r){//建树 
    tree[root].mark=0;
    if(l==r){tree[root].num=1;return;}
    int mid=(l+r)/2;
    build(root*2,l,mid);
    build(root*2+1,mid+1,r);
    tree[root].num=tree[root*2].num+tree[root*2+1].num;
}
void pushdown(int root,int l,int r){//向下传递mark 
    int mid=(l+r)/2;
    int &ma=tree[root].mark;
    if(ma!=0&&l!=r){
        tree[root*2].mark=tree[root*2+1].mark=ma;
        tree[root*2].num=tree[root*2+1].num=0;//该区间内已无树 
        ma=0;
    }
}
int ask(int root,int l,int r,int x,int y){//询问答案,其实不需要这个 直接输出tree[1]即可 
    if(x>r||yreturn 0;
    if(x<=l&&r<=y) return tree[root].num;
    pushdown(root,l,r);
    int mid=(l+r)/2;
    return (ask(root,l,mid,x,y)+ask(root,mid+1,r,x,y));
}
void change(int root,int l,int r,int x,int y,int add){//进行修改 
    if(x>r||yreturn;
    if(x<=l&&r<=y){
        tree[root].mark+=add;
        tree[root].num=0;//该区间内已无树 
        return;
    }
    pushdown(root,l,r);
    int mid=(l+r)/2;
    change(root*2,l,mid,x,y,add);
    change(root*2+1,mid+1,r,x,y,add);
    tree[root].num=tree[root*2].num+tree[root*2+1].num;
}
int main()
{
    n=getin()+1;m=getin();
    //因为坐标轴是从0开始的,所以n+=1 
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int x,y;
        x=getin()+1;y=getin()+1;
        //同理x、y也要加1 
        change(1,1,n,x,y,1);
    }
    printf("%d\n",ask(1,1,n,1,n));//没有必要,直接输出tree[1]即可 
    return 0;
}

  本文由Yzyet编写,网址为www.cnblogs.com/Yzyet。非Yzyet同意,禁止转载,侵权者必究。

转载于:https://www.cnblogs.com/Yzyet/p/6558998.html

你可能感兴趣的:(校门外的树——线段树——大力出奇迹)