1. 程式人生 > >線段樹例題

線段樹例題

線段樹是一種二叉搜尋樹,將原始資料都存在葉節點,依次表示出每個葉節點的根節點。
一般陣列開到葉節點數量的4倍。
關於線段樹的例題
HDU1166 敵兵佈陣
按照指示來就可以
程式碼

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;

#define M 50005

int n;
int a[M];
int sum[M<<2],add[M<<2];//n<<2表示n*4;
void PushUp(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1];//n<<1|1表示n*2+1;根據子節點求根節點 } void Build(int l,int r,int rt) { if(l == r){//指到達葉節點 scanf("%d",&sum[rt]);//輸入葉節點 return; } int m=(l+r)>>1; Build(l,m,rt<<1);//建立左支樹 Build(m+1,r,rt<<1
|1);//建立右支樹 PushUp(rt); } void Update(int L,int C,int l,int r,int rt)//L為要修改的位置,C指要修改的值 { if(l == r){ sum[rt]+=C; return; } int m=(l+r)>>1; if(L <= m) Update(L,C,l,m,rt<<1); else Update(L,C,m+1,r,rt<<1|1); PushUp(rt); } int Query(int L,int
R,int l,int r,int rt) { if(L <= l&&r <= R){//若要查詢的值在範圍內,直接返回 return sum[rt]; } int m=(l+r)>>1; int ans=0; if(L <= m) ans+=Query(L,R,l,m,rt<<1); if(R > m) ans+=Query(L,R,m+1,r,rt<<1|1); return ans; } int main() { int t,k=0; scanf("%d",&t); while(t--){ printf("Case %d:\n",++k); scanf("%d",&n); Build(1,n,1); char op[10]; while(scanf("%s",op)){ if(op[0] == 'E') break; int i,j; scanf("%d%d",&i,&j); if(op[0] == 'Q'){ int ans=Query(i,j,1,n,1); printf("%d\n",ans); } else if(op[0] == 'S'){ Update(i,-j,1,n,1); } else{ Update(i,j,1,n,1); } } } return 0; }

HDU1754 I Hate It
同樣按照要求即可
不同的是上一題是求和,而這一題是要求求出最大的數,所以在根據子節點求根節點的時候要用max
程式碼

#include <stdio.h>
#include <algorithm>
using namespace std;

#define M 200005

int sum[M<<2];

void PushUp(int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);//將子節點中較大的值給根節點
}

void Build(int l,int r,int rt)
{
    if(l == r){
        scanf("%d",&sum[rt]);
    }
    else{
        int m=(l+r)>>1;
        Build(l,m,rt<<1);
        Build(m+1,r,rt<<1|1);
        PushUp(rt);
    }
}

void Update(int L,int C,int l,int r,int rt)
{
    if(l == r){
        sum[rt]=C;//根據題中要求,是將某個位置的值改變為多少,並不是增加或減少
    }
    else{
        int m=(l+r)>>1;
        if(L <= m) Update(L,C,l,m,rt<<1);
        else Update(L,C,m+1,r,rt<<1|1);
        PushUp(rt);
    }
}

int Query(int L,int R,int l,int r,int rt)
{
    if(L <= l&&r <= R){
        return sum[rt];
    }
    int m=(l+r)>>1;
    int ans=0;
    if(L <= m) ans=max(ans,Query(L,R,l,m,rt<<1));//求最大
    if(R > m) ans=max(ans,Query(L,R,m+1,r,rt<<1|1));
    return ans;
}

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        Build(1,n,1);
        for(int i=0;i<m;i++){
            char c[3];//如果只用一個字元可能會造成越界,所以這用一個數組,可以保證輸入時的正確
            int a,b;
            scanf("%s%d%d",c,&a,&b);
            if(c[0] == 'Q'){
                int ans=Query(a,b,1,n,1);
                printf("%d\n",ans);
            }
            else if(c[0] == 'U'){
                Update(a,b,1,n,1);
            }
        }
    }
    return 0;
}

HDU1698 Just a Hook
題意:原來有n個數,都為1,現在對他們進行改變,每次操作輸入x,y,z,代表把【x,y】的值全部改變為z,z可以是1,2,3。
最後輸出n個數的和。
程式碼

#include <stdio.h>
#include <algorithm>
using namespace std;

#define M 100005

int sum[M<<2];
int init[M<<2];//用來存原始資料

void PushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void Build(int l,int r,int rt)
{
    if(l == r){
        sum[rt]=init[l];//給葉節點賦值,都為1
        return;
    }
    int m=(l+r)>>1;
    Build(l,m,rt<<1);
    Build(m+1,r,rt<<1|1);
    PushUp(rt);
}

void Pushdown(int rt,int ln,int rn)//下推標記節點,ln,rn表示左子樹,右子樹的資料數量
{
    if(init[rt]){
        init[rt<<1]=init[rt];
        init[rt<<1|1]=init[rt];
        sum[rt<<1]=init[rt]*ln;
        sum[rt<<1|1]=init[rt]*rn;
        init[rt]=0;
    }
}

void Update(int L,int R,int C,int l,int r,int rt)//要修改的是一個區間的值,所以要進行標記,L,R為該區間的左右端點
{
    if(L <= l&&r <= R){
        sum[rt]=C*(r-l+1);
        init[rt]=C;
        return;
    }
    int m=(l+r)>>1;
    Pushdown(rt,m-l+1,r-m);//下推標記
    if(L <= m) Update(L,R,C,l,m,rt<<1);
    if(R > m) Update(L,R,C,m+1,r,rt<<1|1);
    PushUp(rt);
}

int main()
{
    int t,k=0;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            init[i]=1;
        Build(1,n,1);
        for(int i=0;i<m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(z == 1){
                Update(x,y,1,1,n,1);
            }
            else if(z == 2){
                Update(x,y,2,1,n,1);
            }
            else{
                Update(x,y,3,1,n,1);
            }
        }
        int ans=sum[1];//sum[1]即為所有數的和
        printf("Case %d: The total value of the hook is %d.\n",++k,ans);
    }
    return 0;
}

codeforces 91B Queue
有n只海象排隊,第一隻站在最後的位置,第n只站在第一的位置(從左到右),第i只海象的年齡為ai,找站在他右邊的海象中比他年輕的且離他最遠的和他之間有多少隻海象,(注意即使離他最遠的比他年輕的海象和他之間有年紀比他大的也不會停,知道找到最右端那個比他小的),不存在輸出-1。
還可以用vector做,單調佇列,從第n個數開始遍歷

程式碼

#include <stdio.h>
#include <algorithm>
using namespace std;

#define M 200005
#define maxn 100000005//定義一個很大的值來標記已經遍歷過的資料

int sum[M<<2],a[M<<2];

void PushUp(int rt)
{
    sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);
}

void Build(int l,int r,int rt)
{
    if(l == r){
        scanf("%d",&sum[rt]);
        a[l]=sum[rt];
        return;
    }
    int m=(l+r)>>1;
    Build(l,m,rt<<1);
    Build(m+1,r,rt<<1|1);
    PushUp(rt);
}

void Update(int L,int l,int r,int rt)
{
    if(l == r){
        sum[rt]=maxn;//表示這個值已經尋找過,每次只用遍歷這個值右邊的資料
        return;
    }
    int m=(l+r)>>1;
    if(L <= m) Update(L,l,m,rt<<1);
    else Update(L,m+1,r,rt<<1|1);
    PushUp(rt);
}

int Query(int x,int l,int r,int rt)
{
    if(l == r){
        return l;//返回最右端比他小的值的位置
    }
    int m=(l+r)>>1;
    if(sum[rt<<1|1] < x) Query(x,m+1,r,rt<<1|1);
    else Query(x,l,m,rt<<1);
}

int main()
{
    int n;
    scanf("%d",&n);
    Build(1,n,1);
    for(int i=1;i<=n;i++){
        Update(i,1,n,1);
        if(sum[1] >= a[i])
            printf("%d%c",-1,i == n?'\n':' ');//如果這個數是最小的,便找不到比他小的
        else
            printf("%d%c",Query(a[i],1,n,1)-i-1,i == n?'\n':' ');//求他們之間資料的數量
    }
    return 0;
}