1. 程式人生 > >線段樹經典題目(一定要做完)

線段樹經典題目(一定要做完)

這幾天陸陸續續更新了下邊幾道我所能找到得具有一些代表性的線段樹題目
從最最簡單的區間求和到對區間的各種操作都包涵在這些題目裡了
相信對一些準備學習線段樹的人有一定得幫助
突然發現自己對資料結構的題目非常有感覺,所以在刷下邊的題的同時也生出靈感出了好幾道線段樹題目
等比賽結束後也會陸續加進裡邊


快半年過去程式碼風格也有很大的改變,感覺以前寫的程式碼很不規範,用自己在預定義中定義的一些函式,但後來感覺作用不是很大,所以又刪去了,所以現在看程式碼可能找不到以前我定義的一些函式,本來想更新一下的,無奈這些函式用的太多,改之太過麻煩,所以在此處申明一下
#define LL(x) ((x)<<1)
#define RR(x) ((x)<<1|1)
#define FF(i,n) for(int i = 0 ; i < n ; i ++)
若還有不清楚的地方,只管提出來便是,我一定一一改正

1.hdu1166 敵兵佈陣
更新節點,區間求和。一次AC,做的時候也沒遇見什麼問題!

#include <iostream>  
#include <string>  
using namespace std;  
#define MAX_N 50000  
  
string str;  
int sum;  //記錄總兵數  
int num[MAX_N+1]={0};  //記錄各個兵營的兵數  
  
typedef struct  node  
{  
    int left;  
    int right;  
    int data;  
    node* lchild;  
    node* rchild;  
    node()  
    {  
        left = right = data = 0;  
    }  
}Tree;  
  
Tree* CreateTree(int a,int b)  
{  
    Tree* r;  
    r = (Tree*)malloc(sizeof(Tree));  
  
    r->left = a;  
    r->right = b;  
    if(a == b)  
    {  
        r->data = num[a];  
        r->lchild = r->rchild = NULL;  
    }  
    else  
    {  
        int mid = (a+b)>>1;  
        r->lchild = CreateTree(a,mid);  
        r->rchild = CreateTree(mid+1,b);  
        r->data = r->lchild->data + r->rchild->data;  
    }  
  
    return r;  
}  
  
void insert(Tree* r,int a,int b)  
{  
    if(r->left == a && r->right == a)  
    {  
        r->data += b;  
        return;  
    }  
    int mid = (r->left + r->right)>>1;  
    if(a <= mid)  
    {  
        insert(r->lchild,a,b);  
    }  
    else   
    {  
        insert(r->rchild,a,b);  
    }  
    r->data += b;  
}  
  
void find(Tree* r,int a,int b)  
{  
    if(r->left == a && r->right == b)  
    {  
        sum += r->data;  
        return;  
    }  
    int mid = (r->left + r->right)>>1;  
    if(b<=mid)  
    {  
        find(r->lchild,a,b);  
    }  
    else if(a>mid)  
    {  
        find(r->rchild,a,b);  
    }  
    else  
    {  
        find(r->lchild,a,mid);  
        find(r->rchild,mid+1,b);  
    }  
}  
  
int main()  
{  
    int t,n,x,y;  
    int i;  
    int ca = 0;  
    scanf("%d",&t);  
    while(t--)  
    {  
        printf("Case %d:/n",++ca);  
        scanf("%d",&n);  
        for(i=1;i<=n;i++)  
        {  
            scanf("%d",&num[i]);  
        }  
        Tree* T;  
        T = CreateTree(1,n);  
        while(cin>>str)  
        {  
            if(str == "Query")  
            {  
                sum = 0;  
                scanf("%d%d",&x,&y);  
                find(T,x,y);  
                printf("%d/n",sum);  
            }  
            else if(str == "Add")  
            {  
                scanf("%d%d",&x,&y);  
                insert(T,x,y);  
            }  
            else if(str == "Sub")  
            {  
                scanf("%d%d",&x,&y);  
                insert(T,x,-y);  
            }  
            else  
            {  
                break;  
            }  
        }  
    }  
    return 0;  
}  

.
2.hdu1754 I Hate It
更新節點,區間最值。一開始用連結串列建樹,發現MLE了,就改成了陣列建樹,過!

#include <iostream>  
using namespace std;  
  
#define MAX_N 200000  
  
int big;  
typedef struct node  
{  
    int left;  
    int right;  
    int data;  
    int maxx;  
}node;  
  
node stu[MAX_N*4];  
//int num[MAX_N+1];  
  
int Max(int a,int b)  
{  
    return a>b?a:b;  
}  
  
void CreateTree(int ii,int a,int b)  
{  
    stu[ii].left = a;  
    stu[ii].right = b;  
    stu[ii].maxx = 0;  
    stu[ii].data = 0;  
    if(a == b)  
    {  
        return;  
    }  
    else  
    {  
        int mid = (a+b)>>1;  
        CreateTree(ii*2,a,mid);  
        CreateTree(ii*2+1,mid+1,b);  
    }  
}  
  
void updata(int ii,int a,int b)  
{  
    if(stu[ii].left == a && stu[ii].right == a)  
    {  
        stu[ii].data = b;  
        stu[ii].maxx = b;  
    }  
    else  
    {  
        int mid = (stu[ii].left+stu[ii].right)>>1;  
        if(a <= mid)  
        {  
            updata(ii*2,a,b);  
        }  
        else  
        {  
            updata(ii*2+1,a,b);  
        }  
        if(b > stu[ii].maxx)  
            stu[ii].maxx = b;  
    }  
}  
  
void find(int ii,int a,int b)  
{  
    if(stu[ii].left == a && stu[ii].right == b)  
    {  
        //printf("%d/n",stu[ii].maxx);  
        if(stu[ii].maxx > big)  
            big = stu[ii].maxx;  
    }  
    else   
    {  
        int mid = (stu[ii].left + stu[ii].right)>>1;  
        if(b <= mid)  
        {  
            find(ii*2,a,b);  
        }  
        else if(a > mid)  
        {  
            find(ii*2+1,a,b);  
        }  
        else  
        {  
            find(ii*2,a,mid);  
            find(ii*2+1,mid+1,b);  
        }  
    }  
}  
  
int main()  
{  
    int n,m,num;  
    int i;  
      
    while(scanf("%d%d",&n,&m)!=EOF)  
    {  
        CreateTree(1,1,n);  
        for(i=1;i<=n;i++)  
        {  
            scanf("%d",&num);  
            updata(1,i,num);  
        }  
        char c;  
        int x1,x2;  
        while(m--)  
        {  
            scanf("%*c%c",&c);  
            scanf("%d%d",&x1,&x2);  
            if(c == 'Q')  
            {          
                big = 0x80000000;  
                find(1,x1,x2);  
                printf("%d/n",big);  
            }  
            else  
            {  
                updata(1,x1,x2);  
            }  
        }  
    }  
    return 0;  
}  

.
3.hdu1698 Just a Hook
成段更新,總區間求和.也不是很難,注意遞迴時一層一層的遞迴下去。寫的時候中間忽略了一點,造成錯了好幾次!

#include <iostream>  
using namespace std;  
#define MAX_N 100000  
  
struct node  
{  
    int left;  
    int right;  
    int data;  
    int sum;  
};  
node hook[4*MAX_N];  
  
void CreateTree(int ii,int a,int b)  
{  
    hook[ii].left = a;  
    hook[ii].right = b;  
    if(a == b)  
    {  
        hook[ii].data = 1;  
        hook[ii].sum = 1;  
    }  
    else  
    {  
        int mid = (a+b)>>1;  
        CreateTree(ii*2,a,mid);  
        CreateTree(ii*2+1,mid+1,b);  
        hook[ii].data = 0;  
        hook[ii].sum = (hook[ii*2].sum + hook[ii*2+1].sum);  
    }  
}  
  
void updata(int ii,int a,int b,int c)  
{  
    if(hook[ii].left == a && hook[ii].right == b)  
    {  
        hook[ii].data = c;  
        hook[ii].sum = (b-a+1)*c;  
    }  
    else  
    {  
        if(hook[ii].data > 0)  
        {  
            hook[ii*2].data = hook[ii].data;  
            hook[ii*2].sum = (hook[ii*2].right - hook[ii*2].left+1)*hook[ii*2].data;  
            hook[ii*2+1].data = hook[ii].data;  
            hook[ii*2+1].sum = (hook[ii*2+1].right - hook[ii*2+1].left+1)*hook[ii*2+1].data;  
        }  
        hook[ii].data = 0; //寫的時候這個丟掉了。一直錯  
        int mid = (hook[ii].left + hook[ii].right)>>1;  
        if(b <= mid)  
        {  
            updata(ii*2,a,b,c);  
        }  
        else if(a > mid)  
        {  
            updata(ii*2+1,a,b,c);  
        }  
        else  
        {  
            updata(ii*2,a,mid,c);  
            updata(ii*2+1,mid+1,b,c);  
        }  
        hook[ii].sum = hook[ii*2].sum + hook[ii*2+1].sum;  
    }  
}  
  
  
int main()  
{  
    int t,n,m,x1,x2,x3;  
    int i;  
    scanf("%d",&t);  
    for(i=1;i<=t;i++)  
    {  
        scanf("%d",&n);  
        CreateTree(1,1,n);  
        scanf("%d",&m);  
        while(m--)  
        {  
            scanf("%d%d%d",&x1,&x2,&x3);  
            updata(1,x1,x2,x3);  
        }  
        printf("Case %d: The total value of the hook is %d./n",i,hook[1].sum);  
    }  
    return 0;  
}  

.
6.pku2777 Count Color
成段更新,區間統計,位運算加速(我總把query裡的傳遞給子節點的步驟忘了)。錯了蠻多次的,一開始是RE,應該是中間跑的時候迴圈有問題,後來的話WA,find函式中存在問題!

#include <iostream>  
#include <algorithm>  
using namespace std;  
#define MAX_N 100000  
  
int t;  
int num[35];  
  
typedef struct node  
{  
    int left;  
    int right;  
    int data;  
    node* lchild;  
    node* rchild;  
}Tree;  
  
Tree* CreateTree(int a,int b)  
{  
    Tree* r;  
  
    r=(Tree*)malloc(sizeof(Tree));  
    r->left = a;  
    r->right = b;  
    r->data = 1;  
  
    if(a+1==b)  
    r->lchild=r->rchild=NULL;  
    else  
    {  
    int mid = (a+b)>>1;  
    r->lchild = CreateTree(a,mid);  
    r->rchild = CreateTree(mid,b);  
    }  
      
    return r;  
}  
  
void search(Tree* r,int a,int b,int c)  
{  
    //if(r->data == c)  
    //  return;  
    if(r==NULL)  
        return ;  
    if(r->left == a && r->right == b)  
    {  
        r->data = c;  
    }  
    else  
    {  
        if(r->data > 0)  
        {  
            r->lchild->data=r->data;  
            r->rchild->data=r->data;  
        }  
        r->data = 0;  
        int mid = (r->left + r->right)>>1;  
        if(a >= mid)  
        {  
            search(r->rchild,a,b,c);  
        }  
        else if(b <= mid)  
        {  
      
            search(r->lchild,a,b,c);  
        }  
        else  
        {  
          
            search(r->lchild,a,mid,c);  
            search(r->rchild,mid,b,c);  
        }  
  
        //if(r->lchild->data == r->rchild->data)  
    //  {  
    //      r->data = r->lchild->data;  
    //  }  
    }  
}  
  
void find(Tree* r,int a,int b)  
{  
    /*if(r->left == a && r->right == b) 
    { 
        if(r->data > 0)    
        {    
              
            //num[r->data][0]++;  
            if(num[r->data][0] == 0)    
            {    
                num[r->data][0]++;    
                num[r->data][1] = r->right;    
            }    
            else   
            {    
                if(num[r->data][1] == r->left)    
                    num[r->data][1] = r->right;    
                else   
                {    
                    num[r->data][0]++;    
                    num[r->data][1] = r->right;    
                }    
            }           
        }   
        else if(r->data == 0) 
        { 
            int mid = (a+b)>>1; 
            find(r->lchild,a,mid); 
            find(r->rchild,mid,b); 
        } 
        return; 
    }*/  
    if(r==NULL)  
        return ;  
    if(r->data > 0)  
    {  
        num[r->data]++;  
        return;  
    }  
  
    int mid = (r->left + r->right)>>1;  
    if(a >= mid)  
    {  
          
        find(r->rchild,a,b);  
    }  
    else if(b <= mid)  
    {  
        find(r->lchild,a,b);  
    }  
    else  
    {  
        find(r->lchild,a,mid);  
        find(r->rchild,mid,b);  
    }  
}  
  
int Sum()  
{  
    int i;  
    int sum = 0;  
    for(i=1;i<=t;i++)  
    {  
        if(num[i] != 0)  
            sum++;        
    }  
    return sum;  
}  
  
int main()  
{  
    int l,m;  
      
    while(scanf("%d%d%d",&l,&t,&m)!=EOF)  
    {  
        Tree* T;  
        T = CreateTree(1,l+1);  
        while(m--)  
        {             
            int x1,x2,color;  
            char c;  
            cin>>c;  
            if(c == 'C')  
            {  
                scanf("%d%d%d",&x1,&x2,&color);  
                if(x1>x2)  
                    swap(x1,x2);  
                search(T,x1,x2+1,color);  
            }  
            else  
            {  
                memset(num,0,sizeof(num));  
                scanf("%d%d",&x1,&x2);  
                if(x1>x2)  
                    swap(x1,x2);  
                find(T,x1,x2+1);  
                printf("%d/n",Sum());  
            }  
        }  
    }  
    return 0;  
}  

這題int因為一開始就注意,沒出什麼問題。但錯了很多次,在成段更新的時候不能更新到點,這樣肯定對超時的。這題是要加一個增量,表示該節點增加了多少。現在也不是很明白。線段樹覺的重要的就是node中那些個域!

#include <iostream>  
using namespace std;  
#define MAX_N 100000  
  
__int64 sum = 0;  
__int64 num[MAX_N+1];  
  
struct node  
{  
    int left;  
    int right;  
    __int64 count; //表示該節點的總個數  
    __int64 data; //表示增量的那個數  
};  
  
node N[4*MAX_N];  
  
void CreateTree(int ini,int a,int b)  
{  
    N[ini].left = a;  
    N[ini].right = b;  
    N[ini].data = 0;  
    if(a == b)  
    {  
        N[ini].count= num[a];         
    }  
    else  
    {  
        int mid = (a+b) >> 1;  
        if(b <= mid)  
            CreateTree(ini*2,a,b);  
        else if(a > mid)  
            CreateTree(ini*2+1,a,b);  
        else  
        {  
            CreateTree(ini*2,a,mid);  
            CreateTree(ini*2+1,mid+1,b);  
        }  
        N[ini].count= N[ini*2].count+ N[ini*2+1].count;  
    }  
}  
  
void insert(int ini,int a,int b,int c)  
{  
    if(N[ini].left == a && N[ini].right == b)  
    {  
        N[ini].data += c;  
        //N[ini].count+= (b-a+1) * c;  
    }  
    else  
    {  
        int mid = (N[ini].left + N[ini].right) >> 1;  
        if(b <= mid)  
            insert(ini*2,a,b,c);  
        else if(a > mid)  
            insert(ini*2+1,a,b,c);  
        else  
        {  
            insert(ini*2,a,mid,c);  
            insert(ini*2+1,mid+1,b,c);  
        }  
        N[ini].count = (N[ini*2].count + (N[ini*2].right - N[ini*2].left + 1) * N[ini*2].data)  
            + (N[ini*2+1].count + (N[ini*2+1].right - N[ini*2+1].left + 1) * N[ini*2+1].data);  
    }  
}  
  
void find(int ini,int a,int b)  
{  
    if(N[ini].left == a && N[ini].right == b)  
    {  
        sum += N[ini].count + N[ini].data*(b-a+1);  
    }  
    else  
    {  
        N[ini*2].data += N[ini].data;  
        N[ini*2+1].data += N[ini].data;  
        N[ini].count += N[ini].data * (N[ini].right - N[ini].left + 1);  
        N[ini].data = 0;  
        int mid = (N[ini].left + N[ini].right) >> 1;  
        if(b <= mid)  
            find(ini*2,a,b);  
        else if(a > mid)  
            find(ini*2+1,a,b);  
        else  
        {  
            find(ini*2,a,mid);  
            find(ini*2+1,mid+1,b);  
        }  
    }  
}  
  
void Init()  
{  
    int n,m,x1,x2,x3;  
    char c;  
    int i;  
    while(scanf("%d%d",&n,&m)!=EOF)  
    {  
        for(i=1;i<=n;i++)  
            scanf("%I64d",&num[i]);  
        CreateTree(1,1,n);  
  
        while(m--)  
        {  
            scanf("%*c%c",&c);  
            if(c == 'Q')  
            {  
                sum = 0;  
                scanf("%d%d",&x1,&x2);  
                find(1,x1,x2);  
                printf("%I64d/n",sum);  
            }  
            else   
            {  
                scanf("%d%d%d",&x1,&x2,&x3);  
                insert(1,x1,x2,x3);  
            }  
        }  
    }  
}  
  
int main()  
{  
    Init();  
    return 0;  
}  

一個很噁心的題目,昨天晚上看了題意,上網瞭解了一下離散化。離散化就是為了縮短線段的範圍,如兩個線段(1,6)和(4,9),離散化一下就是1->1,6->3,4->2,9->4,那麼離散後的線段就是(1,3)和(2,4),把線段長度從(1,9)縮短到了(1,4),這種離散化是很實用的。離散的過程就是先把線段的座標儲存下來(1,6,4,9),再排序(1,4,6,9),之後對應(1->1,4->2,6->3,9->4),再根據這個對應修改原先的線段就好了。

寫的時候很噁心,一直re,很久以後試試看的態度改了MAX_N,從10005改成30005就行了。應該是用來排序的那個陣列開小了

#include <iostream>  
#include <algorithm>  
using namespace std;  
#define MAX_N  30005  //一開始開10005 就RE了  
  
int n; //線段個數  
int num[10005];  
int pos[10005][2];  
bool vis[10000005];  
int hash[10000005];  
int set[8*MAX_N];  
  
struct node  
{  
    int left;  
    int right;  
    int data;  
};  
node T[8*MAX_N];  
  
void CreateTree(int ini,int a, int b)  
{  
    T[ini].left = a;  
    T[ini].right = b;  
    T[ini].data = -1;  
    if(a >= b)  
        return;  
    int mid = (a+b)>>1;  
    CreateTree(ini*2,a,mid);  
    CreateTree(ini*2+1,mid+1,b);  
}  
  
void insert(int ini,int a,int b,int color)  
{  
    if(T[ini].left == a && T[ini].right == b)  
    {  
        T[ini].data = color;  
        //return;  
    }  
    else  
    {  
        if(T[ini].data > 0 && T[ini].data != color)  
        {  
            T[ini*2].data = T[ini].data;  
            T[ini*2+1].data = T[ini].data;  
            T[ini].data = 0;  
        }  
        else if(T[ini].data == -1)  
        {  
            T[ini].data = 0;  
        }  
        int mid = (T[ini].left+T[ini].right)>>1;  
        if(b <= mid)   
            insert(ini*2,a,b,color);  
        else if(a > mid)  
            insert(ini*2+1,a,b,color);  
        else  
        {         
            insert(ini*2,a,mid,color);  
            insert(ini*2+1,mid+1,b,color);  
        }     
    }  
}  
  
void find(int ini)  
{  
    if(T[ini].left == T[ini].right)  
    {  
        if(T[ini].data > 0)  
            num[T[ini].data]++;  
  
        return;  
    }  
  
    if(T[ini].data == -1)  
        return;  
    else if(T[ini].data > 0)  
    {  
        num[T[ini].data]++;  
        return;  
    }  
    else if(T[ini].data == 0)  
    {  
        find(ini*2);  
        find(ini*2+1);  
    }  
}  
  
int cmp(int a,int b)  
{  
    return a<b;  
}  
void Init()  
{  
    int i;  
    memset(vis,0,sizeof(vis));  
  
    int  j = 0;  
    scanf("%d",&n);  
    CreateTree(1,1,8*n);  
    for(i=0;i<n;i++)  //離散化  
    {  
        scanf("%d%d",&pos[i][0],&pos[i][1]);  
        if(vis[pos[i][0]] == 0)  
        {  
            set[j++] = pos[i][0];  
            vis[pos[i][0]] = 1;  
        }  
        if(vis[pos[i][1]] == 0)  
        {  
            set[j++] = pos[i][1];  
            vis[pos[i][1]] = 1;  
        }  
    }  
    sort(set,set+j,cmp);  
    for(i=0;i<=j;i++)  
    {  
        hash[set[i]] = i+1;  
    }  
  
    for(i=0;i<n;i++)  
    {  
        pos[i][0] = hash[pos[i][0]];  
        pos[i][1] = hash[pos[i][1]];  
        insert(1,pos[i][0],pos[i][1],i+1);  
    }  
}  
  
int main()  
{  
    int i,t;  
    scanf("%d",&t);  
    while(t--)  
    {  
        memset(num,0,sizeof(num));  
        int sum = 0;  
        Init();  
        find(1);  
        for(i=1;i<=n;i++)  
        {  
            if(num[i] > 0)  
                sum++;  
        }  
        printf("%d/n",sum);  
    }  
    return 0;  
}  

.
9.hdu2795 Billboard
更新節點,詢問特殊(暑假的時候不會線段樹被這水題狂虐)

一開始不能理解,不會建樹,一直以為是用10^9,實際上只要20000*4就行了。而data表示最大值。想通這個就好過了。

#include <iostream>  
using namespace std;  
#define MAX_N 200000  
  
int h,w,n;  
struct node  
{  
    int left;  
    int right;  
    int data;   //表示這一段中最大的數  
};  
node T[4*MAX_N];  
  
int Max(int a,int b)  
{  
    return a>b?a:b;  
}  
  
void CreateTree(int ini,int a,int b)  
{  
    T[ini].left = a;  
    T[ini].right = b;  
    T[ini].data = w; //一開始最大都是寬度  
    if(a == b)  
    {  
        return;  
    }  
    else  
    {  
        int mid = (a + b) >> 1;  
        CreateTree(ini*2,a,mid);  
        CreateTree(ini*2+1,mid+1,b);  
    }  
}  
  
void insert(int ini,int a)  
{  
    if(T[ini].left == T[ini].right)  
    {  
        printf("%d/n",T[ini].left);  
        T[ini].data -= a;  
    }  
    else  
    {  
        if(T[ini*2].data >= a)  
        {  
            insert(ini*2,a);  
        }  
        else  
        {  
            insert(ini*2+1,a);  
        }  
        T[ini].data = Max(T[ini*2].data,T[ini*2+1].data);  
    }  
      
      
}  
  
int main()  
{  
    int i,num;  
    while(scanf("%d%d%d",&h,&w,&n)!=EOF)  
    {  
        if(h < n)  
            CreateTree(1,1,h);  
        else  
            CreateTree(1,1,n);  
      //建樹按照min(h,n)來建,這樣就保證最大時n。  
        for(i=0;i<n;i++)  
        {  
            scanf("%d",&num);  
            if(num > T[1].data)  
            {  
                printf("-1/n");  
            }  
            else  
                insert(1,num);  
        }  
    }  
    return 0;  
}  

.
10.pku3667 Hotel
成段更新,尋找空間(經典型別,求一塊滿足條件的最左邊的空間)

.
12.hdu2871 Memory Control
hotel變形題目,三個都函式一樣(vector忘記清空檢查了好久)

.
14.hdu1542 Atlantis
矩形面積並,掃描線法(發現我們HDU的隊員的矩形面積交模板基本都是在最壞情況下更新到底,寧波慘痛的教訓啊..)

.
15.hdu1255 覆蓋的面積
同上,掃描線法,我多加了一個係數csum,來統計覆蓋兩次的長度(156MS,第一次做線段樹排到第一,紀念下)

.
16.hdu1828 Picture
掃描線,同面積統計,加了一個num_Seg統計一個掃描區域裡的邊

.
19.pku2482 Stars in Your Window
成段更新,區間最值 + 掃描線(轉化成區間最值有點巧妙,資料太TMD的噁心了,中間二分的地方會int溢位,檢查了半個小時)

.
21.pku2464 Brownie Points II
更新節點,區間求和 + 掃描線(很好玩的一道題目,用兩個線段樹沿著掃描線更新,然後按”自己最多,對方最少”的方案一路統計)
(因為兩棵樹,我就直接寫成類了)

.
22.pku3145 Harmony Forever
查詢一個區間內最左邊的數,詢問的val大的話用線段樹,小的話線性掃描,很囧的題目

.
24.pku2991 Crane
記錄了線段的兩端點以及轉過的角度,成段的轉,超有意思的題目,做了之後會對線段樹理解更深刻
(wy教主推薦的,不過我的程式碼寫的太搓了..沒啥學習的價值)

.
25.hdu1823 Luck and Love
二維線段樹,沒啥意思,程式碼量大了一倍而已,題目和運用範圍都沒一維的廣,隨便找了道題目練習下就好