線段樹小結

如果是1~n 個數 那麼我們知道線段樹 最後一層 一定是n個結點! 

那麼 可得線段樹 最多的結點數爲 2^(log2(n)+1)-1 或者 2*n-1個  

單點更新

HDU1754

//9596209	2013-11-16 12:13:35	Accepted	1754	531MS	7184K	1579 B
#include <iostream>
#include<cstdio>
using namespace std;
#define MAX 200002
int val[MAX];
struct Node
{
    int left,right,max;
}tree[3*MAX];
int max(int a,int b)
{
    return a>b?a:b;
}
int creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    if(left==right) return tree[root].max=val[left];
    int a,b,mid;
    mid=(right-left)/2+left;
    a=creat(2*root,left,mid);
    b=creat(2*root+1,mid+1,right);
    return tree[root].max=max(a,b);
}
int calculate(int root,int left,int right)
{
    if(tree[root].left>right || tree[root].right<left)/**兩區間相離,就返回0**/
        return 0;
    if(tree[root].left>=left&&tree[root].right<=right)/**區間在所求區間內部,就返回這個區間的最大值**/
        return tree[root].max;
        int a,b;
    a=calculate(2*root,left,right);
    b=calculate(2*root+1,left,right);
    return max(a,b);
}
int updata(int root,int pos,int v)
{
    if(pos<tree[root].left||pos>tree[root].right)
        return tree[root].max;
    if(pos==tree[root].left&&pos==tree[root].right)
        return tree[root].max=v;
    int a,b;
    a=updata(root*2,pos,v);
    b=updata(root*2+1,pos,v);
    return tree[root].max=max(a,b);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&val[i]);
        creat(1,1,n);
        for(int i=1;i<=m;i++)
        {
            char op;
            int x,y;
            scanf("\n%c%d%d",&op,&x,&y);
            if(op=='Q') printf("%d\n",calculate(1,x,y));
            else updata(1,x,y);
        }
    }
    return 0;
}



HDU1698

修改區間爲定值

//9616564	2013-11-18 20:15:24	Accepted	1698	843MS/906MS	4764K	1995 B
#include <iostream>
#include<cstdio>
using namespace std;
#define MAX 100001
int val[MAX];
struct Node
{
    int left,right;
    int mark;
    int total;
}tree[3*MAX];
int creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    tree[root].mark=0;
    if(left==right)
        return tree[root].total=val[left];
    int mid=(right+left)/2;
    return tree[root].total=creat(2*root,left,mid)+creat(2*root+1,mid+1,right);
}
void up_mark(int root)
{
    if(tree[root].mark)
    {
        tree[root].total=tree[root].mark*(tree[root].right-tree[root].left+1);
        if(tree[root].left!=tree[root].right)
            tree[2*root].mark=tree[2*root+1].mark=tree[root].mark;
        tree[root].mark=0;
    }
}
int up_data(int root,int left,int right,int vol)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return tree[root].total;
    if(tree[root].left>=left&&tree[root].right<=right)
    {
        tree[root].mark=vol;
        up_mark(root);
        return tree[root].total;
        //return tree[root].total=vol*(tree[root].right-tree[root].left+1); //843MS
    }
    return tree[root].total=up_data(2*root,left,right,vol)+up_data(2*root+1,left,right,vol);
}
int calculate(int root,int left,int right)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return 0;
    if(tree[root].left>=left&&tree[root].right<=right)
        return tree[root].total;
    return tree[root].total=calculate(2*root,left,right)+calculate(2*root+1,left,right);
}
int main()
{
    int t;
    int ces=1;
    scanf("%d",&t);
    while(t--)
    {
        int n,q,i;
        scanf("%d%d",&n,&q);
        for(i=1;i<=n;i++)
            val[i]=1;
        creat(1,1,n);
        int x,y,z;
        for(i=1;i<=q;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            up_data(1,x,y,z);
        }
        printf("Case %d: The total value of the hook is %d.\n",ces++,calculate(1,1,n));
    }
    return 0;
}

POJ3468

將區間的數加上某數

#include <iostream>
#include<cstdio>
using namespace std;
#define MAX 100005
int val[MAX];
struct Node
{
    int left,right;
    __int64 mark,total;
}tree[MAX*3];
__int64 creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    tree[root].mark=0;
    if(left==right)
        return tree[root].total=val[left];
    int mid=(right+left)/2;
    return tree[root].total=creat(2*root,left,mid)+creat(root*2+1,mid+1,right);
}
void up_mark(int root)
{
    if(tree[root].mark)
    {
        tree[root].total+=tree[root].mark*(tree[root].right-tree[root].left+1);
        if(tree[root].left!=tree[root].right)
        {
            tree[2*root].mark+=tree[root].mark;
            tree[2*root+1].mark+=tree[root].mark;
        }
        tree[root].mark=0;

    }
}
__int64 up_data(int root,int left,int right,int vol)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return tree[root].total;
    if(tree[root].left>=left&&tree[root].right<=right)
    {
        tree[root].mark=vol;
        up_mark(root);
        return tree[root].total;
        //return tree[root].total+=vol*(tree[root].right-tree[root].left+1);
    }
    return tree[root].total=up_data(2*root,left,right,vol)+up_data(2*root+1,left,right,vol);
}
__int64 calculate(int root,int left,int right)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return 0;
    if(tree[root].left>=left&&tree[root].right<=right)
        return tree[root].total;
    return calculate(2*root,left,right)+calculate(2*root+1,left,right);
}
int main()
{
    int n,q;
    while(~scanf("%d%d",&n,&q))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&val[i]);
        creat(1,1,n);
        while(q--)
        {
            char op;
            scanf("\n%c",&op);
            if(op=='C')
            {
                int x,y,z;
                scanf("%d%d%d",&x,&y,&z);
                up_data(1,x,y,z);
            }
            else
            {
                int x,y;
                scanf("%d%d",&x,&y);
                printf("%I64d\n",calculate(1,x,y));
            }
        }

    }
    return 0;
}


區間染色問題

推薦 浙大ACM培訓資料關於線段樹的PPT

http://wenku.baidu.com/link?url=FWwB9gUJZZxWJuRNDCMcfBW8ebrEx6nRUJJkmqFcSRA_w1XOfv76lgrO-dLvzxvkh_LbXj05Ck1i5S9OBd0RbJBhLSj6lelePYmzSEi0r9W


POJ2528

題意:

有一些海報要貼牆上,按照先後順序貼牆上,可以覆蓋。問最後能看見幾張海報(看見一部分也算)?、

分析:

這題就是:離散化+線段樹 的水題。

染色一個區間就把這個區間 刷成一個值就好,最後遍歷一遍整個區間看有幾個不同的數,就有幾張不同的海報。


//Accepted	1584K	125MS	C++	2835B
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 40000

int sub[MAXN];
struct node
{
    int ll,rr,l,r;
}qu[MAXN];

/**線段樹部分**/

struct Node
{
    int left,right;
    int mark;
    int total;
}tree[3*MAXN];
int creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    tree[root].mark=0;
    if(left==right)
        return tree[root].total=-1;
    int mid=(right+left)/2;
    return tree[root].total=creat(2*root,left,mid)+creat(2*root+1,mid+1,right);
}
void up_mark(int root)
{
    if(tree[root].mark)
    {
        tree[root].total=tree[root].mark*(tree[root].right-tree[root].left+1);
        if(tree[root].left!=tree[root].right)
            tree[2*root].mark=tree[2*root+1].mark=tree[root].mark;
        tree[root].mark=0;
    }
}
int up_data(int root,int left,int right,int vol)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return tree[root].total;
    if(tree[root].left>=left&&tree[root].right<=right)
    {
        tree[root].mark=vol;
        up_mark(root);
        return tree[root].total;
        //return tree[root].total=vol*(tree[root].right-tree[root].left+1); //843MS
    }
    return tree[root].total=up_data(2*root,left,right,vol)+up_data(2*root+1,left,right,vol);
}
int calculate(int root,int left,int right)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return 0;
    if(tree[root].left>=left&&tree[root].right<=right)
        return tree[root].total;
    return tree[root].total=calculate(2*root,left,right)+calculate(2*root+1,left,right);
}


/**線段樹部分**/
int vis[MAXN];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,num=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&qu[i].ll,&qu[i].rr);
            sub[num++] = qu[i].ll;
            sub[num++] = qu[i].rr;
        }
        sort(sub,sub+n*2);
        int X = unique(sub,sub+n*2)-sub;
        creat(1,1,X); /**建樹**/
       int color=0;
        for(int i=1;i<=n;i++)
        {
            qu[i].l = lower_bound(sub,sub+X,qu[i].ll)-sub+1;
            qu[i].r = lower_bound(sub,sub+X,qu[i].rr)-sub+1;
            color++; /**每個區間的顏色都不同!**/
            up_data(1,qu[i].l,qu[i].r,color);/**覆蓋區間**/
        }
        int  ans=0;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=X;i++)/**遍歷染色**/
        {
            int t=calculate(1,i,i);
            //cout<<"----"<<t<<endl;
            if(t!=-1&& !vis[t])
            {
                ans++;
                vis[t] = 1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
/**
10
5
30 1000
2 30
2 12
12 12000
1 2
**/

POJ2777

題意:一個長區間,初始全部染成顏色1,然後有兩種操作:

1. 把區間 A 到 B 刷成 C 顏色。

2. 詢問區間 A 到 B 有多少種不同的顏色。 (A 可能大於 B)

這題是赤裸裸的染色題,學會幾點:

1. 用位運算來處理顏色。

2.延遲下放更新操作。(被延遲標記 坑了N 次WA。。。)


//Accepted	4796K	438MS	G++	2197B	2014-08-18 20:18:59
//Accepted	4272K	344MS	C++	2298B	2014-08-18 20:22:40
#include <iostream>
#include <cstdio>
using namespace std;
/**線段樹部分**/

#define MAX 200001 /**先前開了100萬**/
struct Node
{
    int left,right;
    int mark;
    int statu;
}tree[3*MAX];
int creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    tree[root].mark=-1; /**延遲標記應該標記爲 -1,WA了N次!就是寫成0了!0代表1這種顏色,不能不下放!**/
    if(left==right)
        return tree[root].statu=1;
    int mid=(right+left)>>1;
    return tree[root].statu=creat(2*root,left,mid)|creat(2*root+1,mid+1,right);
}
void up_mark(int root)
{
    if(tree[root].mark>=0)
    {
        tree[root].statu=(1<<tree[root].mark);
        if(tree[root].left!=tree[root].right)
            tree[2*root].mark=tree[2*root+1].mark=tree[root].mark;
        tree[root].mark=-1;
    }
}
int up_data(int root,int left,int right,int vol)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return tree[root].statu;
    if(tree[root].left>=left&&tree[root].right<=right)
    {
        tree[root].mark=vol;
        up_mark(root);
        return tree[root].statu;
    }
    return tree[root].statu=up_data(2*root,left,right,vol)|up_data(2*root+1,left,right,vol);
}
int calculate(int root,int left,int right)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return 0;
    if(tree[root].left>=left&&tree[root].right<=right)
        return tree[root].statu;
    return calculate(2*root,left,right)|calculate(2*root+1,left,right);
}

/**線段樹部分**/
int main()
{
    int L,O,T;
    while(~scanf("%d%d%d",&L,&T,&O))
    {
        creat(1,1,L);
        for(int i=1;i<=O;i++)
        {
            getchar();
            char ch;
            int l,r,color;
            scanf("%c%d%d",&ch,&l,&r);
            if(l>r) swap(l,r);
            if(ch=='C')
            {
              scanf("%d",&color);
              up_data(1,l,r,color-1);
            }
            else
            {
                int st=calculate(1,l,r),ans=0;
                while(st)
                {
                    if(st&1) ans++;
                    st>>=1;
                }
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}


poj3277 

這題需要用到這樣的技巧:

二分建樹時,這樣二分  左子樹(left,middle) 右子樹(middle,right)

把線段樹的葉子節點 設置爲 ( l , l+1);

這樣可以避免  左子樹(left,middle) 右子樹(middle+1,right)

導致的中間段沒加上面積的情況!


//Accepted	7516K	485MS	C++	1752B	2014-09-26 21:51:23
#include <iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 41000
#define LL __int64 // 注意 樓真實下標,樓面積,樓高,都要用LL。線段樹的下標可以用int
struct node1
{
    LL a,b,h;
    bool operator<(const node1 &a )const
    {
        return h<a.h;
    }
}built[MAXN];
LL s[MAXN*2];
int num;
struct node
{
     LL area,mark;
     int l,r;
}tree[MAXN*6]; //因爲一個樓,兩個座標,所以線段樹範圍是 MAXN*2*3
void built_tree(int rt,int l,int r)
{
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].area=0;
    tree[rt].mark=0;
    if(l==r-1) return;
    int m=(l+r)>>1;
    built_tree(rt<<1,l,m);
    built_tree(rt<<1|1,m,r);
}
void upmark(int rt)
{
    if(tree[rt].mark)
    {
        int a=tree[rt].l;
        int b=tree[rt].r;
        tree[rt].area=tree[rt].mark*(s[b]-s[a]);
        if(tree[rt].l!=tree[rt].r-1)
        tree[rt<<1].mark=tree[rt<<1|1].mark=tree[rt].mark;
        tree[rt].mark=0;
    }
}
LL updata(int rt,int l,int r,int h) // 這裏返回沒用LL WA幾發。。
{
    upmark(rt);
    if(tree[rt].l>=r || tree[rt].r<=l)
    return tree[rt].area;
    if(tree[rt].l>=l&&tree[rt].r<=r)
    {
        tree[rt].mark=h;
        upmark(rt);
        return tree[rt].area;
    }
    return tree[rt].area=updata(rt<<1,l,r,h)+updata(rt<<1|1,l,r,h);;
}
int main()
{
    int N;
    scanf("%d",&N);
    num=0;
    for(int i=1;i<=N;i++)
    {
        scanf("%I64d%I64d%I64d",&built[i].a,&built[i].b,&built[i].h);
        s[num++]=built[i].a;
        s[num++]=built[i].b;
    }
    sort(s,s+num);
    int X=unique(s,s+num)-s;
    sort(built+1,built+1+N);
    built_tree(1,0,X);
    for(int i=1;i<=N;i++)
    {
        int a=lower_bound(s,s+X,built[i].a)-s;
        int b=lower_bound(s,s+X,built[i].b)-s;
        updata(1,a,b,built[i].h);
    }
    printf("%I64d\n",tree[1].area);

    return 0;
}



POJ2828

題意:N 個人插隊買票,給出兩個值 id 和 val

表示 這個人插在第 id 個人的後面,他的價值爲 val

問最後的輸出順序。。


分析:

這我真的一開始沒想到是線段樹,真的是題做少了。

如果正向的考慮,那麼每插入一個進來,就會改變相對順序,用鏈表肯定是超時的。

學會逆向思考,

從最後一個人開始,那麼他的位置是肯定確定的,就是他前面必須有 id個人。

那麼同理放好其它的人。

可以看下:

http://www.cnblogs.com/CheeseZH/archive/2012/04/29/2476134.html

的圖示;


//Accepted	10736K	1594MS	C++	1543B
#include <iostream>
#include<cstdio>
using namespace std;
#define MAXN 300000
struct node
{
    int id,val;
}p[MAXN];
/**線段樹部分**/
struct Node
{
    int left,right,blank,val;
}tree[3*MAXN];
int creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    if(left==right) return tree[root].blank=1;
    int a,b,mid;
    mid=(right-left)/2+left;
    a=creat(2*root,left,mid);
    b=creat(2*root+1,mid+1,right);
    return tree[root].blank=a+b;
}
int find(int root,int key,int v)
{
    int l = 2*root;
    int r=  2*root+1;
    if(tree[root].left==tree[root].right)
    {
        tree[root].val = v;
        return tree[root].blank=0;
    }
    if(tree[l].blank < key) find(r,key-tree[l].blank,v);
    else find(l,key,v);
    return tree[root].blank=tree[l].blank+tree[r].blank;
}
int ans[MAXN],num;
void show(int root)
{
    if(tree[root].left==tree[root].right)
    {
        ans[num++] = tree[root].val;
        return ;
    }
    show(2*root);
    show(2*root+1);
}
/**線段樹部分**/
int main()
{
    int N;
    while(~scanf("%d",&N))
    {
        for(int i=1;i<=N;i++)
        {
            scanf("%d%d",&p[i].id,&p[i].val);
        }
        creat(1,1,N);
        num=0;
        for(int i=N;i>=1;i--)
        {
            find(1,p[i].id+1,p[i].val);
        }
        show(1);
        printf("%d",ans[0]);
        for(int i=1;i<num;i++)
        {
            printf(" %d",ans[i]);
        }
        printf("\n");

    }
    return 0;
}


POJ2886

題意:

N個人順時針站成一個圈,每個人手中有一張牌,牌上一個整數(不爲0,可能爲負數),從第k個開始出去,

他手中的整數,若是正整數就是順時針的第幾個繼續出去,負數就是逆時針的第幾個繼續出去。

出去的第 i 個人 , p( i  )指示 i 的約數個數。

問:約數個數最大的 那個人。


分析:

類似約瑟夫環,但是約瑟夫環,報第幾個數出去是固定的。

首先,學到了個新知識--反素數:如果N 是N以內約數個數最多的數,則N就是一個反素數。

題目給出一個 N 即人的個數,那麼只需要打出給出範圍內的 所有反素數就可以找出 N以內約數個數最多的是 第幾個。

現在第幾個人是答案已經出來了,只需要知道這個人的名字就行,就是要知道他是誰。

如果用隊列模擬這個過程,肯定超時,因爲光是一次出隊,都有可能執行 10^8。

上面已經做了個這樣的題,就是線段樹上找位置的。。

那麼同理只需要每次找到位置就行了。。


注意一點:如果牌上是正整數 k ,那麼順時針找到的就是 p+k ,因爲 p 這個人出隊了, 所以p+k的相對順序改變了,所有應該是編號 p+k-1 的人出去。

如果是負數,那麼相對順序不會改變。就是p+k。。 畫個圖就可以明白!


lower_bound(數組頭,數組尾,k) 找出的是第一個小於 k 的數的上一個數下標,或者是等於k的數的下標。

沒注意 RE了好多次。。 

a存的是反素數,b存的是對應a中反素數 的約數個數。

//Accepted	24244K	1344MS	C++	1960B	2014-08-19 16:50:13
#include <iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 600000
const int a[37]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,
25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,500001};
const int b[37]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,100,108,120,128,144,
160,168,180,192,200,1314521};
int N,k;
struct node
{
    char name[20];
    int id;
}stu[MAXN];
/**線段樹部分**/
struct Node
{
    int left,right,blank;
}tree[3*MAXN];
int creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    if(left==right)
    {
         return tree[root].blank=1;
    }
    int a,b,mid;
    mid=(right-left)/2+left;
    a=creat(2*root,left,mid);
    b=creat(2*root+1,mid+1,right);
    return tree[root].blank=a+b;
}
int find(int root,int key,int &ans)
{
    int l = 2*root;
    int r=  2*root+1;
    if(tree[root].left==tree[root].right)
    {
        ans=tree[root].left;
        return tree[root].blank=0;
    }
    if(tree[l].blank < key) find(r,key-tree[l].blank,ans);
    else find(l,key,ans);
    return tree[root].blank=tree[l].blank+tree[r].blank;
}
/**線段樹部分**/
int search(int n)
{
    int tag=lower_bound(a,a+37,n)-a;
    if(n<a[tag]) tag--; /**如果不是等於的情況,這裏就是第一個大於n的數,所以要--**/
    return tag;
}
void solve()
{
    creat(1,1,N);
    int goal=search(N);
    int ID=0; //從標號爲ID 的人開始
    stu[0].id=0;
    for(int i=0;i<a[goal];i++)/**模擬隊**/
    {
        int mod=tree[1].blank;
        if(stu[ID].id>0) k= ((k + stu[ID].id- 2)%mod+mod)%mod + 1;//因爲從1開始所以就多減1 改變了相對順序 總共就-2
        else k= ((k + stu[ID].id-1)%mod+mod)%mod + 1;
        find(1,k,ID);
    }
    printf("%s %d\n",stu[ID].name,b[goal]);
}
int main()
{
    while(~scanf("%d%d",&N,&k))
    {
        for(int i=1;i<=N;i++)
        {
            scanf("%s%d",stu[i].name,&stu[i].id);
        }
        solve();
    }
    return 0;
}





省賽題---高橋低橋

首先建樹肯定拿10^5 的下標建樹!

只需  每次  在橋數組中找洪水的值 的上下界。相當於去淹下標!

最後就遍歷一次各個節點,求出ans。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 110000
int val[MAX];
int n,m,k;
struct Node
{
    int left,right;
    int mark,total;
}tree[MAX*3];
int creat(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    tree[root].mark=0;
    if(left==right)
        return tree[root].total=0;
    int mid=(right+left)/2;
    return tree[root].total=creat(2*root,left,mid)+creat(root*2+1,mid+1,right);
}
void up_mark(int root)
{
    if(tree[root].mark)
    {
        tree[root].total+=tree[root].mark*(tree[root].right-tree[root].left+1);
        if(tree[root].left!=tree[root].right)
        {
            tree[2*root].mark+=tree[root].mark;
            tree[2*root+1].mark+=tree[root].mark;
        }
        tree[root].mark=0;

    }
}
int up_data(int root,int left,int right,int vol)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return tree[root].total;
    if(tree[root].left>=left&&tree[root].right<=right)
    {
        tree[root].mark=vol;
        up_mark(root);
        return tree[root].total;
    }
    return tree[root].total=up_data(2*root,left,right,vol)+up_data(2*root+1,left,right,vol);
}
int calculate(int root,int left,int right)
{
    up_mark(root);
    if(tree[root].left>right || tree[root].right<left)
        return 0;
    if(tree[root].left>=left&&tree[root].right<=right)
        return tree[root].total;
    return calculate(2*root,left,right)+calculate(2*root+1,left,right);
}

int main()
{
    int cas=1;
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        for(int i=1;i<=n;i++) scanf("%d",&val[i]);
        sort(val+1,val+1+n);
        creat(1,1,n);
        int cur=1;
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            int l=upper_bound(val+1,val+n+1,cur)-val;
            int r=upper_bound(val+1,val+n+1,a)-val;   r--;
            cur=b;
            if(l<=r) up_data(1,l,r,1);
        }
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(calculate(1,i,i)>=k) ans++;;
        }
        printf("Case %d: %d\n",cas++,ans);
    }
    return 0;
}


區間合併

HDU1540 

題意:D代表破壞村莊,R代表修復最後被破壞的那個村莊,Q代表詢問包括x在內的最大連續區間是多少

分析:線段樹節點 添加 三個域:當前區間最大連續長度 MAX ,左端點最大連續長度 , 右端點最大連續長度。

R 修復時,用棧來保存就行。

參考代碼:

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

const int maxn = 50000+10;

int n,m;
int s[maxn],top;//s爲模擬棧

struct node
{
    int l,r;
    int ls,rs,ms;//ls,左端最大連續區間,rs右端最大連續區間,ms區間內最大連續區間
} a[maxn<<2];

void init(int l,int r,int i)
{
    a[i].l = l;
    a[i].r = r;
    a[i].ls = a[i].rs = a[i].ms = r-l+1;
    if(l!=r)
    {
        int mid = (l+r)>>1;
        init(l,mid,i*2);
        init(mid+1,r,2*i+1);
    }
}

void insert(int i,int t,int x)
{
    if(a[i].l == a[i].r)
    {
        if(x==1)
            a[i].ls = a[i].rs = a[i].ms = 1;//修復
        else
            a[i].ls = a[i].rs = a[i].ms = 0;//破壞
        return ;
    }
    int mid = (a[i].l+a[i].r)>>1;
    if(t<=mid)
        insert(2*i,t,x);
    else
        insert(2*i+1,t,x);
    a[i].ls = a[2*i].ls;//左區間
    a[i].rs = a[2*i+1].rs;//右區間
    a[i].ms = max(max(a[2*i].ms,a[2*i+1].ms),a[2*i].rs+a[2*i+1].ls);//父親區間內的最大區間必定是,左子樹最大區間,右子樹最大區間,左右子樹合併的中間區間,三者中最大的區間值
    if(a[2*i].ls == a[2*i].r-a[2*i].l+1)//左子樹區間滿了的話,父親左區間要加上右孩子的左區間
        a[i].ls += a[2*i+1].ls;
    if(a[2*i+1].rs == a[2*i+1].r-a[2*i+1].l+1)//同理
        a[i].rs += a[2*i].rs;
}

int query(int i,int t)
{
    if(a[i].l == a[i].r || a[i].ms == 0 || a[i].ms == a[i].r-a[i].l+1)//到了葉子節點或者該訪問區間爲空或者已滿都不必要往下走了
        return a[i].ms;
    int mid = (a[i].l+a[i].r)>>1;
    if(t<=mid)
    {
        if(t>=a[2*i].r-a[2*i].rs+1)//因爲t<=mid,看左子樹,a[2*i].r-a[2*i].rs+1代表左子樹右邊連續區間的左邊界值,如果t在左子樹的右區間內,則要看右子樹的左區間有多長並返回
            return query(2*i,t)+query(2*i+1,mid+1);
        else
            return query(2*i,t);//如果不在左子樹的右邊界區間內,則只需要看左子樹
    }
    else
    {
        if(t<=a[2*i+1].l+a[2*i+1].ls-1)//同理
            return query(2*i+1,t)+query(2*i,mid);
        else
            return query(2*i+1,t);
    }
}

int main()
{
    int i,j,x;
    char ch[2];
    while(~scanf("%d%d",&n,&m))
    {
        top = 0;
        init(1,n,1);
        while(m--)
        {
            scanf("%s",ch);
            if(ch[0] == 'D')
            {
                scanf("%d",&x);
                s[top++] = x;
                insert(1,x,0);
            }
            else if(ch[0] == 'Q')
            {
                scanf("%d",&x);
                printf("%d\n",query(1,x));
            }
            else
            {
                if(x>0)
                {
                    x = s[--top];
                    insert(1,x,1);
                }
            }
        }
    }

    return 0;
}




可持久化線段樹

求第k小(第k大)。現在已經做的分爲2類:(給定1~n的序列)

第一類:不斷詢問 1~m 中的第k小(第k大)。《優先隊列,堆,線段樹,Treap》

第二類:不斷詢問 某一區間 (m1,m2) 的第k小(第k大)。----比前一種更一般的情況。《主席樹,Treap》

第三類:在第二類上還可以不斷修改。----比前一種更一般的情況。


第一類不再贅述啦!

這裏是第二類.

poj2104,poj2761,hdu2665.

#include <iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 100010
int n,m;
int ans[maxn];
struct Node
{
    int a,b,id;
}num[maxn];
bool cmp1(Node A,Node B)
{
    return A.a<B.a;
}
bool cmp2(Node A,Node B)
{
    return A.id<B.id;
}
struct T_node
{
    T_node *left;
    T_node *right;
    int sum;
}*root[maxn],nod[maxn*20];
int tot;
void built(T_node*&p,int l,int r)
{
    p=&nod[tot++];
    p->sum=0;
    if(l==r) return ;
    int m=(l+r)>>1;
    built(p->left,l,m);
    built(p->right,m+1,r);
}
void updata(T_node*p,T_node*&q,int t,int l,int r)
{
    q=&nod[tot++];
    q->left=p->left;
    q->right=p->right;
    q->sum=p->sum+1;
    if(l==r)return ;
    int m=(l+r)>>1;
    if(t<=m) updata(p->left,q->left,t,l,m);
    else       updata(p->right,q->right,t,m+1,r);
}
int query(T_node*p,T_node*q,int k,int l,int r)
{
    if(l==r) return l;
    int m=(l+r)>>1;
    int cnt=q->left->sum - p->left->sum;
    if(k<=cnt)
        return query(p->left,q->left,k,l,m);
    else
        return query(p->right,q->right,k-cnt,m+1,r);
}
void gogogo()
{
    int i;
    tot=0;
    int l,r,k;
    for( i=1;i<=n;i++)
    {
        scanf("%d",&num[i].a);
        num[i].id=i;
    }

    sort(num+1,num+1+n,cmp1);
	/**求出每個的相對大小,並去重另保存ans**/
    int p;
    int sb=1;
    num[1].b=sb;
    ans[sb]=num[1].a;
    p=num[1].a;
    for( i=2;i<=n;i++)
    {
        if(p!=num[i].a)
        {
            p=num[i].a;
            sb++;
        }
        num[i].b=sb;
        ans[sb]=num[i].a;
    }
	/**按id排回來,建線段樹! **/
    sort(num+1,num+1+n,cmp2);
	/**先建一個root[0]樹--相當於模板**/
    built(root[0],1,sb);
	/**序列每個數都建一棵前綴線段樹!**/
	/**( 保存了前一棵的歷史!)**/
    for( i=1;i<=n;i++) updata(root[i-1],root[i],num[i].b,1,sb);/**這裏果然傻逼了!把sb搞成p啦**/
	/**兩個前綴樹相減==所求區間**/
	/**(返回的是相對大小,就到ans裏找序列原來的值)**/
    while(m--)
    {
        scanf("%d%d%d",&l,&r,&k);
        int id=query(root[l-1],root[r],k,1,sb);
        printf("%d\n",ans[id]);
    }
}
int main()
{
   // int T;
    //scanf("%d",&T);
    while(~scanf("%d%d",&n,&m))
    {
       //;
        gogogo();
    }
    return 0;
}



發佈了84 篇原創文章 · 獲贊 7 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章