线段树心得

线段树详解 (原理,实现与应用) 线段树详解 By 岩之痕 一:综述 线段树是一种可以快速进行区间修改和区间查询的数据结构。点修改,区间修改和区间查询的复杂度都是O(log2(n)) 并且,线段树可以维护很多种类的信息。说到线段树就不得不提一下树状数组,树状数组维护的信息必须满足区间减法,因为树状数组只是维护1到X的信息,通过相减来得到区间信息,代码简单而且速度快,占用空间又少于线段树,所以一般情况下可以用树状数组的地方就 阅读详情

线段树用途:

RMQ,线段求长,矩形交,矩形并等……

 

线段树基本操作:

建树,插入,删除,查询,更新,删树

因为它是一棵二叉树,所以它的操作一般除了建树,删树是O(N),其余的都是O(LogN)的。

这个复杂度基本能顺利解决卡时的问题。

 

建树和删树:

这两个是相互影响的过程,有些删树只需要O(1)就行了。

1)节点的内存分配问题

a. 使用动态内存分配,new或者malloc,如果这样,删树往往得消耗O(N)的时间都delete或free

b. 计算节点个数上限,开一个静态的大数组,并且加一个下标或指针来模拟分配。删树时只要将指针或下标回复下即可,单位时间内就实现删树过程。

node mem[MAX];
int  mem_pos  =   0 ;

node 
*
new_node()
{
    node 
* pt = &mem[mem_pos ++];
    memset(pt,
0,sizeof(node));
    
return pt;
}

2)节点信息问题

一般线段树处理区间问题,所以它的信息必定包含左右端点的值,左右子树的指针,如果是RMQ可以加个状态值,如果是处理线段的话,那加的比较多了,一般是覆盖数,测度,重复测度,独立线段数,权和……

总之可以依据题目所需,灵活添加。

但是得注意的是,左右端点值必须是整数,因为实数一是存在精度问题,最重要的是分区间的问题,实数除不尽啊。但是也有解决方法,如果区间数有限的话,可以将实数存在数组里面,用数组下标来表示,这样就能实现实数和整数的映射。而查找也得付出时间消耗,二分也得O(LogN)。

 3)树的表示问题

对于有些静态RMQ,它给予了初始的状态,则可以根据所有状态一次性建造线段树

如:http://acm.hdu.edu.cn/showproblem.php?pid=1754 I Hate It

而像有些未给定初始状态,只是每次会添加一些信息的,可以开始时只建空树。

如:http://acm.hdu.edu.cn/showproblem.php?pid=1823 Luck and Love

上面的题目虽然节点会添加,但是也可以先预先建造一颗逻辑上的线段树结构,保存的信息为空以便之后更新,这需要你知道他的范围以后才可以建造。把这种想法应用到删树的时候,也可以只清空信息域,保留树的逻辑,这样的过程也相当于删除了树,并且省去了下次建树的时间消耗。

 

插入,删除,更新,查询:

简单的更新到点就不用多说了,依据子树的划分确定是访问哪个子树,递归下去,修改叶节点信息,回溯时修改父节点的信息。写起来也是很快的。

说下成段更新,就是更新的也是一个区间,而且该区间较大,更新到点明显就是太耗时了,时间复杂度是O(N)的。

看下http://acm.hdu.edu.cn/showproblem.php?pid=1779 这是内部题目,描述见尾部。

成段更新,并且还得找出最大值的那位,将他的值改为0。

我的做法是,定义三个信息域,mmax保存他子区间的最大值,imax子区间最大值的位置,add本区间累加数。

当找到能完全覆盖它的区间后,在add上加上所给的蜡烛数,回溯时,在他的父节点判断左右子树的值,记录最大值和左右子树中最大值的位置。在父节点判断用的值是子树的add+mmax值。这样就避免了更新到点,只是在经过的路径上累加被加的值。这样就可以保证他的更新复杂度任为O(LogN)。

update(更新区间[l,r], 更新值v)
{
    
if(当本区间刚好是更新区间[l,r]) {
        add 
+= v;
        
return;
    }

    mid 
= (l+r)/2;
    update(左子区间[l,mid], 更新值v);
    update(右子区间[mid
+1,r], 更新值v);
    
if(左子区间mmax+add >= 右子区间mmax+add) {
        mmax 
= 左子区间mmax+add;
        imax 
= 左子区间imax;
    }

    
else {
        mmax 
= 右子区间mmax+add;
        imax 
= 右子区间imax;
    }

    
return;
}

线段树一般都是用来做离散化的,再来看看经典的题目吧。

http://acm.hdu.edu.cn/showproblem.php?pid=1542 Atlantis(矩形交面积)

http://acm.hdu.edu.cn/showproblem.php?pid=1255 覆盖的面积(矩形并面积)

http://acm.hdu.edu.cn/showproblem.php?pid=1828 Picture(矩形轮廓周长)

涉及到二维空间,可以使用二维线段树求解。但是以上3题都可以用一维线段树,另一维经过排序离散化后,按次序扫描,即时间复杂度是O(NLogN)。说下几个上述几题要使用到的信息域。

覆盖数:区间被完全覆盖的次数

测度:区间中线段覆盖的长度

重复测度:区间中覆盖超过一次的线段长度

独立线段树:区间中互不相交的线段数

但二维静态RMQ时,如果只使用一维线段树,那只是从O(N^2)降低成O(NLogN)而已,对于大的数据量来说,还是太慢了。

如:http://acm.zju.edu.cn/show_problem.php?pid=2859 Matrix Searching

使用二维线段树的话,查询时间复杂度可以降为O(LogN LogN),相当的有效了。其实还有O(1)的做法,因为是静态的RMQ,大小桶之类的做法完全可以更为高效。

二维线段树的方法其实也很简单,在节点中再加个 node *类型的指针,指向第二维线段树。

由此可见灵活使用线段树能解决很多复杂问题,而且易于扩充其功能,是居家旅行,必备良药o(∩_∩)o...哈哈

 

 
Problem C (1003)
Happy Children’s Day
Standard Input / Standard Output
Children's Day is coming. In this day, children will get a lot of candies. In MAX city, people develop an automatic candy management (ACM) system. ACM can manage N piles of candies. The system can carry out two operations.
1). I a b c (1<=a<=b<=N, 0<c<=100), ACM will add each pile numbered from a to b with c candies.
2). C a b (1<=a<=b<=N), ACM will choose the pile (from a to b) with the most candies, and give all the candies in that pile to one child. If two or more piles have the most candies, then the pile with smaller id will be chosen.
Given a series of instructions, for each operation C a b, please help the people to find out the number of candies a child can get.
 
Input:
Input may contain several test data sets.
For each data set, the first line contains two integers N, M(0<N,M<=10^5) where N indicates the number of piles, and M indicates the number of operations that follows.
Each of the next M lines describes one operation.
Input will be ended by N=0,M=0,which should not be processed.
 
NOTE: At the beginning of each data set, all of the N piles have 0 candies.
 
Output:
For each operation C a b, print the number of candies the child can get in a line.
 
Sample Input:
5 4
I 1 5 1
C 2 3
I 2 2 4
C 2 3
0 0
 
Sample Output:
1
4
 

 

 附上几篇源码:

// Problem : 1779 Happy Children’s Day
#include  < cstdio >
#include 
< string >
#include 
< algorithm >
using   namespace  std;

struct  node  {
    
int l,r;
    node 
* pl, * pr;
    
int imax;//子区间最大值位置
    int add;//本区间累加值
    int mmax;//记录子区间最大值
}
mem[ 300000 ];
int  mem_pos;

node 
*
new_node()
{
    node 
* pt = &mem[mem_pos ++];
    memset(pt,
0,sizeof(node));
    
return pt;
}


node 
*
make_tree(
int  il,  int  ir)
{
    node 
* root = new_node();
    root 
->l = il;
    root 
->r = ir;
    root 
->imax = il;
    
if(ir > il) {
        
int mid = (il+ir)/2;
        root 
->pl = make_tree(il, mid);
        root 
->pr = make_tree(mid+1, ir);
    }

    
return root;
}


int
update(node 
*  root, int  l,  int  r,  int  v)
{
    
if(root ->l == l && root ->r == r) {
        root 
->add += v;
        
return root ->mmax + root ->add;
    }

    
int mid = (root ->l + root ->r)/2;

    
if(l <= mid) {
        
if(r <= mid) {
            
int t = update(root ->pl, l, r, v);
            
if(t >= root ->pr ->add + root ->pr ->mmax) {
                root 
->mmax = t;
                root 
->imax = root ->pl ->imax;
            }

            
else {
                root 
->mmax = root ->pr ->add + root ->pr ->mmax;
                root 
->imax = root ->pr ->imax;
            }

        }

        
else {
            root 
->mmax = update(root ->pl, l, mid, v);
            root 
->imax = root ->pl ->imax;
            
int t = update(root ->pr, mid+1, r, v);
            
if(t > root ->mmax) {
                root 
->mmax = t;
                root 
->imax = root ->pr ->imax;
            }

        }

    }

    
else {
        
int t = update(root ->pr, l, r, v);
        
if(t > root ->pl ->add + root ->pl ->mmax) {
            root 
->mmax = t;
            root 
->imax = root ->pr ->imax;
        }

        
else {
            root 
->mmax = root ->pl ->add + root ->pl ->mmax;
            root 
->imax = root ->pl ->imax;
        }

    }


    
return root ->mmax + root ->add;
}


int
get (node  *  root, int  l,  int  r, int   & imax)
{
    
if(root ->l == l && root ->r == r) {
        imax 
= root ->imax;
        
return root ->mmax + root ->add;
    }

    
int mid = (root ->l + root ->r)/2;
    
int t1,t2;
    
if(l <= mid) {
        
if(r <= mid) {
            t1 
= get(root ->pl, l,r,imax);
        }

        
else {
            
int imax2;
            t1 
= get(root ->pl, l,mid,imax);
            t2 
= get(root ->pr, mid+1,r,imax2);
            
if(t2 > t1) {
                t1 
= t2;
                imax 
= imax2;
            }

        }

    }

    
else {
        t1 
= get(root ->pr, l,r,imax);
    }

    
return t1 + root ->add;
}



int  main()
{
    
int t,n,m,i,j;
    
int a,b,c;
    
char cmd[5];
    
int imax;
    mem_pos 
= 0;
    node 
* root = make_tree(1, 100010);
    
while(scanf("%d %d", &n,&m)==2) {
        
if(n==0 && m==0) {
            
break;
        }

        
for(i=0;i<mem_pos;i++) {
            mem[i].imax 
= mem[i].l;
            mem[i].add 
= mem[i].mmax = 0;
        }

        getchar();
        
while(m --) {
            scanf(
"%s",cmd);
            
if(cmd[0] == 'I') {
                scanf(
"%d %d %d",&a,&b,&c);
                update(root, a,b,c);
            }

            
else {
                
int cand;
                scanf(
"%d %d",&a,&b);
                imax 
= 0;
                printf(
"%d ",cand = get(root, a,b,imax));
                
if(imax != 0) {
                    update(root, imax, imax, 
-cand);
                }

                
//printf("get pile = %d, num = %d ", imax,cand);
            }

        }

    }

}

 

// Problem : 1828 ( Picture ) 
#include  < cstdio >
#include 
< string >
#include 
< algorithm >
using   namespace  std;

struct  node  {
    
int l, r;//左右端点
    int il, ir;
    node 
* pl, * pr;//左右子树
    int len;//测度
    int cover;//完全覆盖线段数
    int lines;//独立线段数
    int lbd, rbd;//左右端点覆盖
}
mem[ 40000 ];
int  mem_pos;

struct  line  {
    
int x,y1,y2;
    
int flag;
    
bool operator < (const line & tt ) const {
        
if(x != tt.x) {
            
return x < tt.x;
        }

        
return flag > tt.flag;
    }

}
list[ 11000 ];

int  yaxis[ 11000 ];

node 
*
new_node()
{
    node 
* pt = &mem[mem_pos ++];
    memset(pt,
0,sizeof(node));
    
return pt;
}


void
cal_len(node 
*  root)
{
    root 
->len = 0;
    
if(root ->cover > 0) {
        root 
->len = root ->r - root ->l;
    }

    
else {
        
if(root ->pl && root ->pr) {
            root 
->len = root ->pl ->len + root ->pr ->len;
        }

    }

}


void
cal_line(node 
*  root)
{
    root 
->lbd = root ->rbd = root ->lines = 0;
    
if(root ->cover > 0) {
        root 
->lbd = root ->rbd = root ->lines = 1;
    }

    
else {
        
if(root ->pl && root ->pr) {
            root 
->lbd = root ->pl ->lbd;
            root 
->rbd = root ->pr ->rbd;
            root 
->lines = root ->pl ->lines + root ->pr ->lines
                        
- (root ->pl ->rbd * root ->pr ->lbd); 
        }

    }

}


node 
*
make_tree(
int  axis[],  int  il,  int  ir)
{
    node 
* root = new_node();
    root 
->l = axis[il];
    root 
->r = axis[ir];
    root 
->il = il;
    root 
->ir = ir;
    
if(ir - il > 1) {
        
int mid = (il+ir)/2;
        root 
->pl = make_tree(axis, il, mid);
        root 
->pr = make_tree(axis, mid, ir);
    }

    
return root;
}


void
update(node 
*  root, line e)
{
    
if(e.y1 <= root ->l && root ->r <= e.y2) {
        root 
->cover += e.flag;
    }

    
if(root ->ir - root ->il > 1) {
        
int mid = (root ->il + root ->ir)/2;
        
if(e.y1 < yaxis[mid]) {
            update(root 
->pl, e);
        }

        
if(yaxis[mid] < e.y2) {
            update(root 
->pr, e);
        }

    }

    cal_len(root);
    cal_line(root);
}


int  main()
{
    
int t,n,i;
    
int ls;
    
int x1,x2,y1,y2;
    
int sum;
    
while(scanf("%d", &n)==1) {
        ls 
= 0;
        mem_pos 
= 0;
        sum 
= 0;
        
for(i=0;i<n;i++) {
            scanf(
"%d %d %d %d", &x1,&y1,&x2,&y2);
            list[ls].x 
= x1;
            list[ls].y1 
= y1;    list[ls].y2 = y2;
            list[ls].flag 
= 1;
            list[ls
+1].x = x2;
            list[ls
+1].y1 = y1;    list[ls+1].y2 = y2;
            list[ls
+1].flag = -1;
            yaxis[ls] 
= y1;
            yaxis[ls
+1] = y2;

            ls 
+= 2;
        }

        sort(yaxis, yaxis
+ls);
        sort(list, list
+ls);
        
        
int preLen = 0, preLines = 0;
        node 
* root = make_tree(yaxis, 0, ls-1);
        
for(i=0;i<ls;i++) {
            update(root, list[i]);
            sum 
+= abs(root ->len - preLen);
            sum 
+= 2 * preLines * (list[i].x - list[i-1].x);
            
//printf("(%d,%d) %d (%d,%d) ", root->len, root->lines, sum, preLen,preLines);
            preLen = root ->len;
            preLines 
= root ->lines;
        }

        printf(
"%d ", sum);
    }

}

 

// Problem : 1255 ( 覆盖的面积 ) 
#include  < cstdio >
#include 
< string >
#include 
< algorithm >
using   namespace  std;

struct  node  {
    
double l,r;
    node 
* pl, * pr;
    
double len;//测度
    int cover;//完全覆盖数
    double relen;//重复测度
}
mem[ 10000 ];
int  mem_pos;

struct  line  {
    
double x,y1,y2;
    
int flag;
    
bool operator < (const line & tt ) const {
        
return x < tt.x;
    }

}
list[ 2100 ];
double  yaxis[ 2100 ];

node 
*
new_node()
{
    node 
* pt = &mem[mem_pos ++];
    memset(pt,
0,sizeof(node));
    
return pt;
}


void
cal_len(node 
*  root)
{
    root 
->len = 0;
    
if(root ->cover > 0) {
        root 
->len = root ->r - root ->l;
    }

    
else {
        
if(root ->pl) {
            root 
->len += root ->pl ->len;
        }

        
if(root ->pr) {
            root 
->len += root ->pr ->len;
        }

    }

}


void
cal_relen(node 
*  root)
{
    root 
->relen = 0;
    
if(root ->cover > 1) {//当本线段被完全覆盖多次
        root ->relen = root ->r - root ->l;
    }

    
else if(root ->cover == 1) {//当本线段被完全覆盖一次
        if(root ->pl) {
            root 
->relen += root ->pl ->len;
        }

        
if(root ->pr) {
            root 
->relen += root ->pr ->len;
        }

    }

    
else {//未被覆盖过
        if(root ->pl) {
            root 
->relen += root ->pl ->relen;
        }

        
if(root ->pr) {
            root 
->relen += root ->pr ->relen;
        }

    }

}


node 
*
make_tree(
int  il,  int  ir)
{
    node 
* root = new_node();
    root 
->l = yaxis[il];
    root 
->r = yaxis[ir];
    
if(ir - il > 1) {
        
int mid = (il+ir)/2;
        root 
->pl = make_tree(il, mid);
        root 
->pr = make_tree(mid, ir);
    }

    
return root;
}


void
update(node 
*  root, line e)
{
    
if(root ->l == e.y1 && root ->r == e.y2) {
        root 
->cover += e.flag;
        cal_len(root);
        cal_relen(root);
        
return ;
    }

    
if(root ->pl ->r > e.y1) {//[l,r)
        if(root ->pl ->r >= e.y2) {
            update(root 
->pl, e);
        }

        
else {
            line e2 
= e;
            e2.y2 
= root ->pl ->r;
            update(root 
->pl, e2);
            e2 
= e;
            e2.y1 
= root ->pr ->l;
            update(root 
->pr, e2);
        }

    }

    
else {
        update(root 
->pr, e);
    }

    cal_len(root);
    cal_relen(root);
}


int  main()
{
    
int t,n,i;
    
int ls;
    
double x1,x2,y1,y2;
    
double sum, unions, intersections;
    scanf(
"%d", &t);
    
while(t --) {
        ls 
= 0;
        mem_pos 
= 0;
        sum 
= unions = intersections = 0;
        scanf(
"%d", &n);
        
for(i=0;i<n;i++) {
            scanf(
"%lf %lf %lf %lf", &x1,&y1,&x2,&y2);
            list[ls].x 
= x1;
            list[ls].y1 
= y1;    list[ls].y2 = y2;
            list[ls].flag 
= 1;
            list[ls
+1].x = x2;
            list[ls
+1].y1 = y1;    list[ls+1].y2 = y2;
            list[ls
+1].flag = -1;
            yaxis[ls] 
= y1;
            yaxis[ls
+1] = y2;
            ls 
+= 2;
            sum 
+= (x2 - x1)*(y2 - y1);
        }

        sort(yaxis, yaxis
+ls);
        sort(list, list
+ls);
        
        node 
* root = make_tree(0, ls-1);
        update(root, list[
0]);
        
for(i=1;i<ls;i++) {
            intersections 
+= root ->relen * (list[i].x - list[i-1].x);
            update(root, list[i]);
        }

        printf(
"%.2lf ", intersections);
    }

}

 

 

// Problem : 1542 ( Atlantis ) 
#include  < cstdio >
#include 
< string >
#include 
< algorithm >
using   namespace  std;

struct  node  {
    
double l,r;
    node 
* pl, * pr;
    
double len;
    
int cover;
}
mem[ 10000 ];
int  mem_pos;

struct  line  {
    
double x,y1,y2;
    
int flag;
    
bool operator < (const line & tt ) const {
        
return x < tt.x;
    }

}
list[ 2100 ];
double  yaxis[ 2100 ];

node 
*
new_node()
{
    node 
* pt = &mem[mem_pos ++];
    memset(pt,
0,sizeof(node));
    
return pt;
}


void
cal_len(node 
*  root)
{
    root 
->len = 0;
    
if(root ->cover > 0) {
        root 
->len = root ->r - root ->l;
    }

    
else {
        
if(root ->pl) {
            root 
->len += root ->pl ->len;
        }

        
if(root ->pr) {
            root 
->len += root ->pr ->len;
        }

    }

}


node 
*
make_tree(
int  il,  int  ir)
{
    node 
* root = new_node();
    root 
->l = yaxis[il];
    root 
->r = yaxis[ir];
    
if(ir - il > 1) {
        
int mid = (il+ir)/2;
        root 
->pl = make_tree(il, mid);
        root 
->pr = make_tree(mid, ir);
    }

    
return root;
}


void
update(node 
*  root, line e)
{
    
if(root ->l == e.y1 && root ->r == e.y2) {
        root 
->cover += e.flag;
        cal_len(root);
        
return ;
    }

    
if(root ->pl ->r > e.y1) {//[l,r)
        if(root ->pl ->r >= e.y2) {
            update(root 
->pl, e);
        }

        
else {
            line e2 
= e;
            e2.y2 
= root ->pl ->r;
            update(root 
->pl, e2);
            e2 
= e;
            e2.y1 
= root ->pr ->l;
            update(root 
->pr, e2);
        }

    }

    
else {
        update(root 
->pr, e);
    }

    cal_len(root);
}


int  main()
{
    
int t,n,i;
    
int ls;
    
double x1,x2,y1,y2;
    
double sum, unions, intersections;
    
//scanf("%d", &t);
    t = 1;
    
while(1) {
        ls 
= 0;
        mem_pos 
= 0;
        sum 
= unions = intersections = 0;
        scanf(
"%d", &n);
        
if(n==0) break;
        
for(i=0;i<n;i++) {
            scanf(
"%lf %lf %lf %lf", &x1,&y1,&x2,&y2);
            list[ls].x 
= x1;
            list[ls].y1 
= y1;    list[ls].y2 = y2;
            list[ls].flag 
= 1;
            list[ls
+1].x = x2;
            list[ls
+1].y1 = y1;    list[ls+1].y2 = y2;
            list[ls
+1].flag = -1;
            yaxis[ls] 
= y1;
            yaxis[ls
+1] = y2;
            ls 
+= 2;
            sum 
+= (x2 - x1)*(y2 - y1);
        }

        sort(yaxis, yaxis
+ls);
        sort(list, list
+ls);
        
        node 
* root = make_tree(0, ls-1);
        update(root, list[
0]);
        
for(i=1;i<ls;i++) {
            unions 
+= root ->len * (list[i].x - list[i-1].x);
            update(root, list[i]);
        }

        printf(
"Test case #%d Total explored area: %.2lf ", t ++, unions);
        
//printf("%.2lf ", sum - unions);
    }

}

 

// Matrix Searching
#include  < cstdio >
#include 
< string >
#include 
< algorithm >
using   namespace  std;

int  t,n,q;
int  matrix[ 310 ][ 310 ];
struct  node  {
    
int l,r;
    node 
* pl, * pr;
    node 
* ytree;
    
int mmin;
}
mem[ 400000 ];
int  mem_pos;

node 
*
new_node()
{
    node 
* pt = &mem[mem_pos ++];
    memset(pt,
0,sizeof(pt));
    
return pt;
}


node 
*
make_tree(
int  x1, int  x2, int  y1, int  y2, bool  flag)
{
    node 
* root = new_node();
    
if(flag) {// first dimension
        root ->l = x1;
        root 
->r = x2;
        root 
->ytree = make_tree(x1,x2,y1,y2,false);
        
if(x1 != x2) {
            
int mid = (root ->l + root ->r)/2;
            root 
->pl = make_tree(x1, mid,y1,y2,true);
            root 
->pr = make_tree(mid+1, x2,y1,y2,true);
        }

    }

    
else {// second dimension
        root ->l = y1;
        root 
->r = y2;
        
if(y1 != y2) {
            
int mid = (root ->l + root ->r)/2;
            root 
->pl = make_tree(x1,x2,y1,mid,false);
            root 
->pr = make_tree(x1,x2,mid+1,y2,false);
            root 
->mmin = min(root ->pl ->mmin, root ->pr ->mmin);
        }

        
else {
            root 
->mmin = matrix[x1][y1];
            
int i;
            
for(i=x1+1;i<=x2;i++) {
                root 
->mmin = min(root ->mmin, matrix[i][y1]);
            }

        }

    }

    
return root;
}


int
query(node 
*  root,  int  x1, int  x2, int  y1, int  y2, bool  flag)
{
    
int t1,t2;
    
int mid = (root ->l + root ->r)/2;
    
if(flag) {// first dimension
        if(root ->l == x1 && root ->r == x2) {
            
return query(root ->ytree, x1,x2,y1,y2,false);
        }

        
else {
            
if(x2 <= mid) {
                
return query(root ->pl,x1,x2,y1,y2,true);
            }

            
if(x1 > mid) {
                
return query(root ->pr,x1,x2,y1,y2,true);
            }

            t1 
= query(root ->pl,x1,mid,y1,y2,true);
            t2 
= query(root ->pr,mid+1,x2,y1,y2,true);
        }

    }

    
else {// second dimension
        if(root ->l == y1 && root ->r == y2) {
            
return root ->mmin;
        }

        
else {
            
if(y2 <= mid) {
                
return query(root ->pl,x1,x2,y1,y2,false);
            }

            
if(y1 > mid) {
                
return query(root ->pr,x1,x2,y1,y2,false);
            }

            t1 
= query(root ->pl,x1,x2,y1,mid,false);
            t2 
= query(root ->pr,x1,x2,mid+1,y2,false);
        }

    }

    
return min(t1,t2);
}


int  main()
{
    
int i,j,k;
    
int c1,c2,r1,r2;
    scanf(
"%d",&t);
    
while(t --) {
        scanf(
"%d", &n);
        
for(i=1;i<=n;i++) {
            
for(j=1;j<=n;j++) {
                scanf(
"%d", &matrix[i][j]);
            }

        }

        mem_pos 
= 0;
        node 
* root = make_tree(1,n,1,n,true);
        scanf(
"%d", &q);
        
while(q --) {
            scanf(
"%d %d %d %d",&r1,&c1,&r2,&c2);
            printf(
"%d ", query(root,r1,r2,c1,c2,true));
        }

    }

}
线段树详解 线段树详解、懒标记线段树、动态开点线段树 阅读详情

相关推荐

线段树--概念+模板+入门题

引子 子问题–1 给出n个数,m次问询,每次问询给出一个范围l、r,求区间l–r的总和 很容易想到用前缀和可以解决 O(n) 子问题–2 给出n个数,m次修改,每次给出一个范围l、r和一个值k,将区间l—r的数全部+k,m次修改后,再问询1次求区间l–r的总和 也可以想到这用差分就能解决 O(n) 最后将这两个问题结合起来,给出n个数,m次操作,操作有两种一个是将区间l–r的值+k,一个是询问区间l–r的总和。 这个问题与之前不同的地方是,之前想要求一个范围只需处理一次数据算前缀和,然后数据不变,可以一

Rikkaの博客 3693

【学习笔记】详解线段树(浅显易懂,匠心之作,图文并茂)

超详解线段树(浅显易懂,几乎涵盖所有线段树类型讲解,匠心之作,图文并茂)

weq2011的博客 3万+

Agent测试框架Harbor指南

Harbor是一个AI Agent单元测试框架,用于自动化测试AI代理在容器环境中的任务执行能力。它支持18+种AI代理,通过沙箱隔离和自动验证机制评估任务完成情况。任务必须具有可编程验证的输出结果,适合代码、数据、系统等可量化任务,不适合创意写作等主观任务。Harbor采用CLI操作方式,核心架构包含Job管理、Trial执行和验证器三个层级,执行流程包括环境启动、Agent执行和结果验证。任务目录包含指令文件、配置文件、Docker环境定义和测试脚本等必需组件,通过测试脚本自动判断任务完成质量。

loo_Charles_ool的博客 1386

算法数据结构——线段树(Segment Tree)精讲及python实现

线段树(Segment Tree):一种基于分治思想的二叉树,用于在区间上进行信息统计。它的每一个节点都对应一个区间leftright通常是整数。每一个叶子节点表示了一个单位区间(长度为1),叶子节点对应区间上。每一个非叶子节点的左子节点表示的区间都为,右子节点表示的的区间都为。线段树是一棵平衡二叉树,树上的每个节点维护一个区间。根节点维护的是整个区间,每个节点维护的是父亲节点的区间二等分之后的其中一个子区间。当有n。

白话机器学习 5167

信贷审批中的那些坑:从零开始搭建风控策略体系的5个关键步骤

本文深入探讨了信贷审批中的风控策略体系搭建,从数据陷阱识别、规则冲突诊断到反欺诈攻防,提供了5个关键步骤的实战指南。重点解析了如何通过特征工程、决策树优化和模型可解释性技术,构建动态平衡的风控体系,有效降低逾期风险并提升审批效率。

weixin_29253711的博客 411

nvlddmkm事件ID14报错排查全指南:从找不到描述到显卡驱动崩溃

系统日志中的“无法找到描述”往往让人一头雾水,但这类提示其实隐藏着驱动故障的关键线索。Windows事件查看器通过事件源和ID记录系统异常,其中显卡驱动相关的内核模式驱动组件一旦超时失败,就会触发TDR机制,产生13、14、153等不同事件ID。理解这些ID的含义,比纠结“找不到描述”本身更重要。对于频繁出现的驱动崩溃问题,可以从日志频率和发生场景判断方向,再通过安全模式下彻底清理驱动、关闭冲突软件、检查供电和温度等步骤逐层定位。无论是游戏满载黑屏,还是睡眠唤醒后闪退,掌握NVIDIA驱动日志的解读逻辑,都

weixin_30545945的博客 403

权值线段树(知识学习+板子总结)

概念 和线段树一样的函数, p这个区间里维护的是这段区间里的数出现的次数 每出现一个数x,叶子结点[x,x]这个区间就+1,删除就-1 支持动态查询第k大的数、数x出现的最小rank、 寻找x的前驱(即小于x的最大的数) 和寻找x的后继(即大于x的最小的数)等操作 思路来源 https://www.bilibili.com/video/av16552942?from=search&...

小衣同学的博客 1506

5g LDPC编译码-LDPC编码

本文摘要:LDPC编码是5G业务信道的核心编码技术,属于线性分组码。5G中LDPC编码分为两种基图类型(BG1和BG2),通过码块分割、CRC处理等步骤实现。编码过程主要涉及校验矩阵生成和校验位计算,其中校验矩阵采用稀疏矩阵结构。文中详细介绍了5G LDPC编码的具体实现流程,包括码块分割规则、校验位生成方法,以及两种基图(BG1和BG2)在矩阵结构和参数设置上的差异,重点阐述了通过模块化方法求解校验比特位的技术路径。

weixin_38270585的博客 1363

2020.01.18【NOIP提高组】模拟A 组总结

估分:100+0+100=200 实际:50+0+70=120 T1:3856. 【NOIP2014八校联考第3场第1试10.4】规避(path) 对于在凸多边形上的点,一定是绕着多边形的边走最优,对于两个相邻的节点连上边,之后,对于两个多边形上的点,判断这条线段是否与另一个多边形相交,方法是枚举异于两个点所在多边形的多边形的两个不相邻节点,再求出两条线段所在直线的解析式,判断交点是否在两条...

zsjzliziyang的博客 1541

AURIX TC397开发板到手第一步:用iLLD库驱动LED流水灯(附完整代码)

本文详细介绍了如何使用英飞凌AURIX TC397开发板和iLLD库实现LED流水灯效果。从开发环境配置到硬件连接,再到完整的代码实现,逐步引导开发者完成项目。文章还提供了优化建议和常见问题解决方案,帮助嵌入式开发新手快速上手。

weixin_30911451的博客 390

AutoSAR系列讲解 - 前言

博主xyfx是一名汽车行业的嵌入式软件工程师,本职工作是同步电机的驱动控制(主要做FOC的,这个大家要是有兴趣,以后可以单独开一个专栏来讲解一下),由于做控制器的时候需要用到AutoSAR,所以特意的去学习了相关知识,同时博主自己也在同步提高中,有写的不合理的地方也请大家指正。我准备对AutoSAR开设两个大的方面来讲解,一是AutoSAR的入门篇,二是AutoSAR的实践篇,分别讲述AutoSAR的理论基础和工具的使用。

雪云飞星的博客 4万+

【算法思想·二叉搜索树】特性篇

所谓BST的特性,关键就是: 1、BST本身特性,左孩子值 < 父节点值 < 右孩子值。 2、基于1,BST中序遍历(左中右)结果是升序排列节点值,反向中序遍历(右中左)则是降序排列。

Captain823Jack的博客 1013

智能制造之物料详解

制造业业务系统中的物料流转贯穿需求、采购、生产到交付全流程,各系统协同实现精准追踪。需求阶段由CRM/ERP/PLM生成物料计划;采购入库阶段SCM/WMS/QMS完成采购质检;生产阶段MES/APS协调领料加工;成品交付阶段WMS/SCM处理出库物流。整个流程以BOM为蓝图,ERP为调度中心,通过数据联动实现物料状态、位置和数量的全链路可追溯。

cdfunlove的博客 654

数据结构之线段树

文章目录线段树(区间树)Segment Tree线段树的概念为什么要使用线段树手写一个线段树线段树中的区间查询线段树问题 LeetCode303LeetCode307对区间进行操作的时间复杂度 线段树(区间树)Segment Tree 线段树的概念 线段树是一种特殊的树结构。这种数据结构主要用于解决“线段”或者是“区间”这种特殊的数据 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些...

qq_24095055的博客 1227

PCB仿真软件:Altium Designer_(18).PCB设计中的热管理

在PCB设计中,热管理是一个至关重要的环节。随着电子设备的集成度越来越高,功耗和发热量也随之增加,不合理的热设计可能导致电路板过热,从而影响电路的性能和可靠性。热管理不仅能够提高电路板的使用寿命,还能确保电路在各种环境条件下的稳定工作。本节将详细介绍如何在Altium Designer中进行有效的热管理,包括热分析、热设计和热仿真等关键步骤。

kkchenjj的博客 1984

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

线段树(从入门到进阶)

首先,讲解线段树之前,应该了解到线段树应该是一种工具,可以将一些对于区间(或者线段)的修改、维护,从O(N)的时间复杂度变成O(logN)。 一.线段树概念引入 线段树是一种二叉树,也就是对于一个线段,我们会用一个二叉树来表示。比如说一个长度为5的线段,我们可以表示成这样: 这代表着如果你要表示线段的和,那么最上面的根节点权值就等于两个儿子节点的权值之和。 那么就可以得到:节点i的权值 = i节点的左儿子权值 + i节点的右儿子权值 二.简单线段树 三.区间+/-修改与区间查询 四.区间乘除修改与查询

hahahahahaha5的博客 2972

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

线段树学习

线段树是一种能把一些对于区间(或者线段)的修改、维护,从O(n)的时间复杂度变为O(logn)的工具。线段树是一个完全二叉树,我们可以用堆来存,可以用一维数组来存。对于一个长度为8的线段,可以这样表示每一个线段[L, R],代表区间[L, R]的和,如[1,4]就代表1~4的和如果用一维数组来存,下标(蓝色)如下:由此我们可以知道,对于下标为x的线段,有以下性质以数组a为例:父节点:a[x/2] 或者 a[x >> 1]左儿子:a[2 * x] 或者 a[x

m0_62652820的博客 1876

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

线段树详解(附图解,模板及经典例题分析)

线段树是一种常用来维护区间信息的数据结构 ​ 可以在 O(logn) 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间的最大值,求区间的最小值)等操作 ​ 如下图所示,线段树是建立在区间基础上的树,树的每个节点都代表着一段区间【L,R】

qq_63786973的博客 3896

线段树详解(含代码实现经过测试)

1.线段树介绍 什么是线段树?线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。[1] 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。 使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。--..

qq_56999918的博客 2224

线段树和树状数组

线段树与树状数组

2302_80364740的博客 2093

线段树(SegmentTree)

线段树(segment tree)顾名思义, 是用来存放给定区间(segment or interval)内对应信息的一种数据结构。

数据工匠记 831

线段树模板(史上最简单易懂的数据结构)

什么是线段树? 线段树言下之意就是将线段分解为一颗树,并且这颗树是一颗二叉树,树上的每一个节点都具有某些性质。 线段树有什么用? 线段树主要能够在O(logn)的时间复杂度上解决区间上的问题。 题目引入:AcWing243.一个简单的整数问题2 本题目就是很简单的线段树的板子题,可以用来当做模板来使用。 线段树的几个重要操作: 1.tree[]数组的建立: struct node{ int l;//表示左端点 int r;//表示右端点 int v;//表示区间内的...

weixin_54442315的博客 3774

线段树的四种模板

第一种模板只适合单点修改的题目,每次修改都传递到树的子节点。对于区间修改则不合适,同时,由于数组大小事先申明好,其点数也有上限。为了解决数据量的问题,这里提供了动态开点,解除了每个树的元素都占一个固定坑位的限制。但是仍然有限制,点数不可以随便加,有上限。和第一套模板相比,增加了区间修改的功能。但是点数仍然有上限,数据量顶多十万,再往上就不能适用了。点数无限制,需要的时候申请,但是效率会很低。...

lhm的博客 4483
上一篇: 敌兵布阵 1166 HDU 线段树
下一篇: hdu 1698 Just a Hook (线段树)
翔蝈蝈
博客等级 码龄15年 25粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值