1. 程式人生 > >簡單線段樹

簡單線段樹

\n void poj3468 增加 out nod sum 下標 mes

一、單點更新

hdu1166區間和

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
const int maxn=50005;
const int maxnnode=1<<19;
struct node{
    int value;
    int left,right;
}node[maxnnode];

int father[maxn];

void BuidTree(int i,int l,int r)
{
    node[i].left
=l; node[i].right=r; node[i].value=0; if(l==r) { father[l]=l; return ; } BuidTree(i<<1,l,(int)floor((l+r)/2.0)); BuidTree((i<<1)+1,(int)floor((l+r)/2.0+1),r); } void UpdataTree(int ri,int x) { if(ri==1)return; int fi=ri/2; node[fi].value
+=x; UpdataTree(fi,x); } int res=0; void Query(int i,int l,int r) { if(node[i].left==l&&node[i].right==r) { res+=node[i].value; return; } i<<1; if(l<=node[i].right) { if(r<=node[i].right) Query(i,l,r); else Query(i,l,node[i].right); } i
++; if(r<=node[i].left) { if(l<=node[i].left) Query(i,l,r); else Query(i,node[i].left,r); } } int main() { int t; cin >> t; while(t--){ int n,tmp,k=1; cin >> n; BuidTree(1,1,n); for(int i=0;i<n;i++) { cin >> tmp; node[father[tmp]].value=tmp; UpdataTree(father[tmp],tmp); } cout << "Case " << k << ":" << endl; while(1) { string op; int a,b; cin >> op >> a >> b; if(op[0]==Q) Query(1,a,b); else if(op[0]==S) UpdataTree(father[a],-b); else if(op[0]==A) UpdataTree(father[a],b); else break; } k++; } return 0; }

二、區間更新

POJ3468區間和,Lazy標記只有當操作到該節點時才將標價下放;

#include <iostream>
#include <cstdio>

using namespace std;
typedef long long ll;
const int maxn=1e5+10;

#define lson l,m,i<<1
#define rson m+1,r,i<<1|1

ll sum[maxn<<2],add[maxn<<2];        //區間和,區間結點增加值;

struct Node{
    int l,r;
    int mid(){
        return (l+r)>>1;
    }
}tree[maxn<<2];

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

void PushDown(int rt,int m)
{
    if(add[rt]){                     //如果該區間結點標記存在,向下推移;
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=add[rt]*(m-(m>>1));
        sum[rt<<1|1]+=add[rt]*(m>>1);
        add[rt]=0;
    }
}

void BuidTree(int l,int r,int i)   //建樹;
{
    tree[i].l=l;
    tree[i].r=r;
    add[i]=0;
    if(l==r){
        scanf("%I64d",&sum[i]);
        return;
    }
    int m=tree[i].mid();
    BuidTree(lson);
    BuidTree(rson);
    PushUp(i);
}
void UpdataTree(int c,int l,int r,int rt)
{
    if(tree[rt].l==l&&tree[rt].r==r){          //符合查詢區間條件;
        add[rt]+=c;
        sum[rt]+=(ll)c*(r-l+1);
        return;
    }
    if(tree[rt].l==tree[rt].r)return;
    PushDown(rt,tree[rt].r-tree[rt].l+1);   //將標記下移;
    int m=tree[rt].mid();
    if(r<=m)UpdataTree(c,l,r,rt<<1);        //遞歸向下查找匹配區間;
    else if(l>m)
        UpdataTree(c,l,r,rt<<1|1);
    else{
        UpdataTree(c,l,m,rt<<1);
        UpdataTree(c,m+1,r,rt<<1|1);
    }
    PushUp(rt);
}

ll Query(int l,int r,int rt)
{
    if(l==tree[rt].l&&r==tree[rt].r)return sum[rt];
    PushDown(rt,tree[rt].r-tree[rt].l+1);
    int m=tree[rt].mid();
    ll res=0;
    if(r<=m)res+=Query(l,r,rt<<1);          //匹配合適的查找區間;
    else if(l>m)res+=Query(l,r,rt<<1|1);
    else{
        res+=Query(l,m,rt<<1);
        res+=Query(m+1,r,rt<<1|1);
    }
    return res;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        BuidTree(1,n,1);
        while(m--){
            char ch[3];
            int a,b,c;
            scanf("%s",ch);
            //cout << ch << endl;
            if(ch[0]==Q)
            {
                scanf(" %d %d",&a,&b,&c);
                printf("%lld\n",Query(a,b,1));
            }else{
                scanf(" %d %d %d\n",&a,&b,&c);
                UpdataTree(c,a,b,1);
            }
        }
    }
    return 0;
}

三、線段樹+離散化

poj2528

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=20005;
const int MAX=1e7+5;
#define lson left,m,rt<<1
#define rson m+1,right,rt<<1|1
//海報從後往前貼,避免後面的海報的幹擾;
int pl[maxn],pr[maxn];      //存區間端點
int port[maxn],id[MAX];     //離散化端點
bool covered[maxn<<2];      //存線段樹的區間信息
struct node{
    int l,r;
    int mid(){
        return (l+r)>>1;
    }
}tree[maxn<<2];

void BuidTree(int left,int right,int rt)
{
    tree[rt].l=left;
    tree[rt].r=right;
    covered[rt]=false;
    if(left>=right)
        return ;
    int m=tree[rt].mid();
    BuidTree(lson);
    BuidTree(rson);
}

bool query(int left,int right,int rt)
{
    if(covered[rt])return false;
    if(left==tree[rt].l&&right==tree[rt].r){
        covered[rt]=true;
        return true;
    }
    int m=tree[rt].mid();
    bool res;
    if(right<=m){
        res=query(left,right,rt<<1);
    }else if(left>m){
        res=query(left,right,rt<<1|1);
    }else{
        bool res1=query(left,m,rt<<1);
        bool res2=query(m+1,right,rt<<1|1);
        res=res1||res2;
    }
    if(covered[rt<<1]&&covered[rt<<1|1])    covered[rt]=true;
    return res;
}
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--){
        int cnt=0,ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&pl[i],&pr[i]);
            port[cnt++]=pl[i];
            port[cnt++]=pr[i];
        }
        sort(port,port+cnt);
        cnt=unique(port,port+cnt)-port;     //離散化;
        for(int i=0;i<cnt;i++)  id[port[i]]=i+1;

        BuidTree(1,cnt,1);
        for(int i=n-1;i>=0;i--){
            //cout << i <<endl;
            if(query(id[pl[i]],id[pr[i]],1)){
                ans++;
                //cout << ans << endl;
            }
        }
        cout <<ans << endl;
    }
    return 0;
}

離散化:有些數據本身很大, 自身無法作為數組的下標保存對應的屬性。如果這時只是需要這堆數據的相對屬性, 那麽可以對其進行離散化處理。當數據只與它們之間的相對大小有關,而與具體是多少無關時,可以進行離散化。比如當你數據個數n很小,數據範圍卻很大時(超過1e9)就考慮離散化更小的值,能夠實現更多的算法。

//1.用數組離散
for(int i=1;i<=n;i++){
    cin>>a[i].val;
    a[i].id = i;
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
    b[a[i].id] = i;     //將a[i]數組映射成更小的值,b[i]就是a[i]對應的rank值
    
//2.用STL+二分離散化
for(int i=1;i<=n;i++){
    cin>>a[i];
    b[i] = a[i];
}
sort(b+1,b+1+n);
int len = unique(b+1,b+1+n)-b-1;   //len就是去重之後的數組長度,unique用法可以去網上看看,用法簡單
for(int i=1;i<=n;i++)
    a[i] = lower_bound(b+1,b+1+n,a[i])-b;   //a[i]就是直接離散化出來的數組

簡單線段樹