zoukankan      html  css  js  c++  java
  • 初学线段树(poj3264+poj2777)

    线段树是用来对一堆数据处理的树结构,它的核心思想是二分(一般都用递归实现)。

    树里需要存的是区间的左右,还有就是看题目需要了

    二叉树特性:1:左子树编号是父树的2倍,右子树是父树的2倍加一 。
                  2:左子树l是父树的l,左子树的r是父树的(l+r)/2;右子树的l是父树的(l+r)/2 +1,右子树的r是父树的r

    如图:

    图是偷来的,推荐那个大佬的博客吧:http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html

    先看下面这个例题: 

    现在给定一组有序数列(数据量一般都超级大),然后问你某个区间的值(是不是觉得用前缀和就可以写?但是他还有另外一个条件),而且在询问你的过程中,他还可以对序列中的数进行更改,再接着询问你某个区间的和。(现在应该不能用前缀和写了吧,毕竟数据量一大,它改了某个点的值,那你就要改与这个点有关的和全部改变)。这时候,你就可以用线段树来写,它是将区间二分,是一个满二叉树。

    看代码吧。

     1 #include<iostream>
     2 #include<cstdio>
     3 #define Maxn 50010
     4 using namespace std;
     5 int kp;
     6 //:树
     7 struct Tree{
     8     int l;//(l,r) 
     9     int r;
    10     int sum;//(l,r)上的和 
    11 }tree[Maxn]; 
    12 //1:建树
    13 //:树特性:1:左子树编号是父树的2倍,右子树是父树的2倍加一 。
    14 //2:左子树l是父树的l,左子树的r是父树的(l+r)/2;右子树的l是父树的(l+r)/2 +1,右子树的r是父树的r 
    15 void Btree(int k,int l,int r)//k:节点编号,l:左,r:右
    16 {
    17     tree[k].l=l,tree[k].r=r;
    18     if(l==r){
    19         scanf("%d",&kp);
    20         tree[k].sum=kp;
    21         return ;
    22     }
    23     int mid=(l+r)/2;
    24     Btree(k*2,l,mid);
    25     Btree(k*2+1,mid+1,r);
    26     tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;
    27 }
    28 //查询
    29 int ans=0;
    30 void query(int k,int start,int ed){//要查询的区间 (start,ed)
    31     
    32     if(start<=tree[k].l&&ed>=tree[k].r){
    33         ans+=tree[k].sum;
    34         return ;
    35     }
    36     
    37     //考虑左走还是右走,还是左右都走 
    38     int mid=(tree[k].l+tree[k].r)/2; 
    39     if(ed<=mid){//
    40         query(k*2,start,ed);
    41         
    42     }else if(start>=mid+1){//
    43         query(k*2+1,start,ed);
    44         
    45     }else{//左加右 
    46         query(k*2,start,ed);
    47         query(k*2+1,start,ed);
    48          
    49     }
    50     return ;
    51 }
    52 
    53 //更改
    54 void change(int k,int aim,int val){//aim更改的元素下标 ,val是把aim元素更改为val 
    55     
    56     if(tree[k].l==tree[k].r&&aim==tree[k].l){
    57         tree[k].sum=val;
    58         return ;
    59     } 
    60     int mid=(tree[k].r+tree[k].l)/2;
    61     
    62     if(aim>=tree[k].l&&aim<=mid){//l,r要一直逼近aim==tree[k].l,aim==tree[k].r 
    63         change(k*2,aim,val);
    64         
    65     }else{
    66         change(k*2+1,aim,val);
    67     } 
    68     
    69     tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;//回溯时改变值 
    70 }
    71 int main(){
    72     int n,k=1;
    73     cin>>n;
    74     Btree(1,1,n);//建立一个根节点区间是【1,n】的树
    75     //**********演示***************** 
    76     for(int i=1;i<=7;i++){
    77         cout<<"   ("<<tree[i].l<<" , "<<tree[i].r<<")   "<<tree[i].sum<<endl;
    78     }
    79     query(1,1,3);//询问 
    80     cout<<ans<<endl;
    81     ans=0;
    82     change(1,1,100);//改变一个值 
    83     for(int i=1;i<=7;i++){
    84         cout<<"   ("<<tree[i].l<<" , "<<tree[i].r<<")   "<<tree[i].sum<<endl;
    85     }
    86     query(1,1,3);//再次询问 
    87     cout<<ans<<endl;
    88     return 0; 
    89 }
    View Code

    输出:

    下面有两道入门题。

    poj3246

    Balanced Lineup
    Time Limit: 5000MS   Memory Limit: 65536K
    Total Submissions:68003   Accepted: 31585
    Case Time Limit: 2000MS

    Description

    For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

    Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

    Input

    Line 1: Two space-separated integers, N and Q
    Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i 
    Lines N+2..N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

    Output

    Lines 1..Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

    Sample Input

    6 3
    1
    7
    3
    4
    2
    5
    1 5
    4 6
    2 2

    Sample Output

    6
    3
    0

    Source

    思路:它只需将前面存和改成存最大最小值即可

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #define Maxn 50010
     4 using namespace std;
     5 int kp;
     6 //:树
     7 struct Tree{
     8     int l;//(l,r) 
     9     int r;
    10     int sum;//(l,r)上的和 
    11     int minn,maxx;
    12 }tree[Maxn*4];//因为是树,所以它是比题目给的点的数据最大还要大,开4倍就一定够 
    13 //1:建树
    14 //:树特性:1:左子树编号是父树的2倍,右子树是父树的2倍加一 。
    15 //2:左子树l是父树的l,左子树的r是父树的(l+r)/2;右子树的l是父树的(l+r)/2 +1,右子树的r是父树的r 
    16 void Btree(int k,int l,int r)//k:节点编号,l:左,r:右
    17 {
    18     tree[k].l=l,tree[k].r=r;
    19     if(l==r){
    20         scanf("%d",&kp);
    21         tree[k].maxx=tree[k].minn=kp;
    22         return ;
    23     }
    24     int mid=(l+r)/2;
    25     Btree(k*2,l,mid);
    26     Btree(k*2+1,mid+1,r);
    27     tree[k].maxx=max(tree[k*2].maxx,tree[k*2+1].maxx);
    28     tree[k].minn=min(tree[k*2].minn,tree[k*2+1].minn);
    29 }
    30 //查询
    31 int Max,Min;
    32 void query(int k,int start,int ed){//要查询的区间 (start,ed)
    33     
    34     if(start<=tree[k].l&&ed>=tree[k].r){
    35         Max=max(tree[k].maxx,Max);
    36         Min=min(tree[k].minn,Min);
    37         return ;
    38     }
    39     
    40     //考虑左走还是右走,还是左右都走 
    41     int mid=(tree[k].l+tree[k].r)/2; 
    42     if(ed<=mid){//
    43         query(k*2,start,ed);
    44         
    45     }else if(start>=mid+1){//
    46         query(k*2+1,start,ed);
    47         
    48     }else{//左加右 
    49         query(k*2,start,ed);
    50         query(k*2+1,start,ed);
    51          
    52     }
    53     return ;
    54 }
    55 
    56 //更改
    57 void change(int k,int aim,int val){//aim更改的元素下标 ,val是把aim元素更改为val 
    58     
    59     if(tree[k].l==tree[k].r&&aim==tree[k].l){
    60         tree[k].sum=val;
    61         return ;
    62     } 
    63     int mid=(tree[k].l+tree[k].r)/2;
    64     if(aim>=tree[k].l&&aim<=mid){//l,r要一直逼近aim==l,aim==r 
    65         change(k*2,aim,val);
    66         
    67     }else{
    68         change(k*2+1,aim,val);
    69         
    70     } 
    71     
    72     tree[k].maxx=max(tree[k*2].maxx,tree[k*2+1].maxx);
    73     tree[k].minn=min(tree[k*2].minn,tree[k*2+1].minn);
    74 }
    75 int main(){
    76     int n,q;
    77     int l,r;
    78     cin>>n>>q;
    79     Btree(1,1,n);
    80     while(q--){
    81         scanf("%d%d",&l,&r);
    82         Max=-1,Min=0x3f3f3f3f;
    83         query(1,l,r);
    84         printf("%d
    ",Max-Min);
    85     }
    86     return 0;
    87 }
    View Code

    poj2777

    Count Color
    Time Limit: 1000MS   Memory Limit: 65536K
    Total Submissions:53502   Accepted: 16107

    Description

    Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem. 

    There is a very long board with length L centimeter, L is a positive integer, so we can evenly divide the board into L segments, and they are labeled by 1, 2, ... L from left to right, each is 1 centimeter long. Now we have to color the board - one segment with only one color. We can do following two operations on the board: 

    1. "C A B C" Color the board from segment A to segment B with color C. 
    2. "P A B" Output the number of different colors painted between segment A and segment B (including). 

    In our daily life, we have very few words to describe a color (red, green, blue, yellow…), so you may assume that the total number of different colors T is very small. To make it simple, we express the names of colors as color 1, color 2, ... color T. At the beginning, the board was painted in color 1. Now the rest of problem is left to your. 

    Input

    First line of input contains L (1 <= L <= 100000), T (1 <= T <= 30) and O (1 <= O <= 100000). Here O denotes the number of operations. Following O lines, each contains "C A B C" or "P A B" (here A, B, C are integers, and A may be larger than B) as an operation defined previously.

    Output

    Ouput results of the output operation in order, each line contains a number.

    Sample Input

    2 2 4
    C 1 1 2
    P 1 2
    C 2 2 2
    P 1 2
    

    Sample Output

    2
    1
    

    Source

     
    题意:给你一块长木板,将木板分成编号1~L的L段,然后C是代表将编号为A和B之间的木板颜色改为C,P代表它询问你编号为A和B之间的木板颜色一共有几种(!!!注意输入的并不能保证是前面的那个数小于后面那个,即A不一定小于B,所以区间是[min(A,B),max(A,B)])。
     
    思路:这题因为颜色只有30种,所以可以用二进制存颜色,即用二进制中的1的位置代表是什么颜色,这样存下颜色在算有几种颜色时可用位运算“ | ”运算,因为它只要相应的二进制位有1,那他相应位就为1,这样就不用会算到重复的颜色。至于线段树中存的内容就是color(颜色)(二进制,即颜色种类的数目)和l,r。然后就是按题目要求改变某区间的颜色,这里有一个十分重要的知识,就是“懒标记”。这在我上面推荐的博客上也有,那位大佬讲的十分好(http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html)。
     
    剩下的看代码吧:
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<string.h>
      4 #define Maxn 100010
      5 #define LL long long
      6 using namespace std;
      7 //线段树懒标记要改的地方都有!!! 
      8 struct Tree{
      9     int l,r;
     10     int k;
     11     int f;
     12     LL color;
     13 }tree[Maxn*5];
     14 
     15 void BTree(int k,int l,int r){
     16     tree[k].l=l,tree[k].r=r;
     17     if(l==r){
     18         tree[k].color=2;//因为是二进制存颜色,输入是1~T,所以这写1,2都没错 
     19         return ;
     20     }
     21     
     22     int mid=(l+r)/2;
     23     BTree(k*2,l,mid);
     24     BTree(k*2+1,mid+1,r);
     25     
     26     tree[k].color=(tree[k*2].color|tree[k*2+1].color);// 更新父节点颜色数 
     27     
     28 }
     29 
     30 void push_down(int k){//懒标记的下传操作 
     31     if(tree[k].l==tree[k].r) return ; 
     32     tree[k*2].f=tree[k].f;//要将父节点保留的懒标记传给子节点 
     33     tree[k*2+1].f=tree[k].f;
     34     tree[k*2].color=1<<tree[k].f;//更新节点颜色 
     35     tree[k*2+1].color=1<<tree[k].f;
     36     tree[k].f=0;//删除节点的懒标记 
     37 }
     38 
     39 void change(int k,int start,int ed,int aim){
     40     
     41     if(tree[k].l>=start&&tree[k].r<=ed){
     42         tree[k].color=1<<aim;//颜色种类为1<<aim 
     43         tree[k].f=aim;//!!! 懒标记,存下颜色 
     44         return ;
     45     }
     46     if(tree[k].f) push_down(k);//!!! 如果当前k编号节点有懒标记,就push_down 
     47     
     48     int mid=(tree[k].l+tree[k].r)/2;
     49     if(ed<=mid){
     50         change(k*2,start,ed,aim);
     51     }else if(start>mid){
     52         change(k*2+1,start,ed,aim);
     53     }else{
     54         change(k*2,start,ed,aim);
     55         change(k*2+1,start,ed,aim);
     56     }
     57     tree[k].color=(tree[k*2].color|tree[k*2+1].color); //更新父节点颜色数 
     58 }
     59 
     60 LL ans;
     61 
     62 void query(int k,int start,int ed){
     63     if(tree[k].l>=start&&tree[k].r<=ed){
     64         ans=(tree[k].color|ans);    
     65         return ;
     66     }
     67     
     68     if(tree[k].f) push_down(k);//!!! 同上 
     69     
     70     int mid=(tree[k].l+tree[k].r)/2;
     71     
     72     if(mid>=ed){
     73         query(k*2,start,ed);
     74     }else if(mid<start){
     75         query(k*2+1,start,ed);
     76     }else{
     77         query(k*2,start,ed);
     78         query(k*2+1,start,ed);
     79     }
     80 }
     81 
     82 int main(){
     83     memset(tree,0,sizeof(tree));
     84     int L,T,O;
     85     char s;
     86     int A,B,C;
     87     scanf("%d%d%d",&L,&T,&O);
     88     getchar();
     89     BTree(1,1,L);
     90     while(O--){
     91         scanf("%c",&s);
     92         
     93         if(s=='C'){
     94             scanf("%d%d%d",&A,&B,&C);
     95             getchar();
     96             change(1,min(A,B),max(A,B),C);
     97         }else if(s=='P'){
     98             scanf("%d%d",&A,&B);
     99             getchar();
    100             ans=0;
    101             query(1,min(A,B),max(A,B));//!这比较坑,要注意 
    102             int cnt=0;
    103             while(ans){//计算ans的二进制中有几个1 
    104                 int h=ans%2;
    105                 ans>>=1;
    106                 if(h) cnt++;
    107             }
    108             printf("%d
    ",cnt);
    109         }
    110     }
    111     return 0;
    112 }
    113 /*
    114 8 20 10
    115 C 5 5 5
    116 C 6 6 6
    117 C 7 7 7
    118 C 8 8 8
    119 C 5 7 9
    120 P 5 8
    121 C 4 5 10
    122 P 4 7
    123 P 4 8
    124 P 3 7
    125 */
    View Code

     懒操作的效果演示:

    在上面代码改变和询问下都加上树的输出,如下:

        while(O--){
            scanf("%c",&s);
            
            if(s=='C'){
                scanf("%d%d%d",&A,&B,&C);
                getchar();
                change(1,min(A,B),max(A,B),C);
                //***************演示************ 
                for(int i=1;i<=15;i++){
                    cout<<tree[i].l<<"   "<<tree[i].r<<"   "<<tree[i].color<<endl;
                }
            }else if(s=='P'){
                scanf("%d%d",&A,&B);
                getchar();
                ans=0;
                query(1,min(A,B),max(A,B));
                //*********************************** 
                for(int i=1;i<=15;i++){
                    cout<<tree[i].l<<"   "<<tree[i].r<<"   "<<tree[i].color<<endl;
                }
                int cnt=0;
                while(ans){//计算ans的二进制中有几个1 
                    int h=ans%2;
                    ans>>=1;
                    if(h) cnt++;
                }
                printf("%d
    ",cnt);
            }
        }

    输出如下:

    有没有发现在改变颜色时并没有改变全部,而是只改变了相应区间和它的父树,而在询问时却在改变一些本该改变时要改变的区间(也没有全部改变,只改变了与询问区间有关的区间(要是没怎么懂就画图看看吧!)),这样就大大减少了不必要的操作。

  • 相关阅读:
    jmeter--同步定时器
    手机配置jmete器
    jmeter---http授权管理器
    jmeter---http信息头管理器
    mysql---聚合函数
    jmeter---BeanSheel提取数组
    简述MYSQL的优化
    java常用设计模式
    什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用?
    String s = new String("xyz");创建了几个String Object? 二者之间有什么区别?
  • 原文地址:https://www.cnblogs.com/liuzuolin/p/10490900.html
Copyright © 2011-2022 走看看