1. 程式人生 > >Magician(hdu 5316 線段樹區間合併)

Magician(hdu 5316 線段樹區間合併)

題目連結:

Magician

 

題意:

給定一個數組,定義beautiful subsequence為一個序列,裡面每個元素在給定陣列中的下標是奇偶交替的。

有2種操作:

0 求區間[a,b]中滿足beautiful subsequence的序列中,序列所有元素之和的最大值。

1 把原陣列中下標為a的那個元素的值改為b。

 

思路:

線段樹單點更新+區間合併

一個區間內的beautiful subsequence只有4種情況,奇開頭奇結尾、奇開頭偶結尾、偶開頭奇結尾和偶開頭偶結尾。因此,對於每段區間,我們只要儲存這4種情況的最大值,就可以根據此進行區間合併,比如奇結尾的只能跟偶開頭的合併。最後,取這4種情況中的最大值即可。

 

code:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int MAX = 100000 + 100;
const ll inf = 1e18;

typedef struct{
    ll ex[3][3];
    ll val[3][3];
}Point;

ll n,m;
Point tree[MAX<<2];

//區間合併
Point pushup(Point a,Point b)
{
    Point c;
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            c.ex[i][j]=a.ex[i][j]|b.ex[i][j];
            if(a.ex[i][j]&&b.ex[i][j])
                c.val[i][j]=max(a.val[i][j],b.val[i][j]);
            else if(a.ex[i][j])
                c.val[i][j]=a.val[i][j];
            else if(b.ex[i][j])
                c.val[i][j]=b.val[i][j];
        }
    }
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            for(int k=0;k<2;k++){
                if(a.ex[i][j]&&b.ex[!j][k]){
                    if(c.ex[i][k])
                        c.val[i][k]=max(c.val[i][k],a.val[i][j]+b.val[!j][k]);
                    else{
                        c.ex[i][k]=1;
                        c.val[i][k]=a.val[i][j]+b.val[!j][k];
                    }
                }
            }
        }
    }
    return c;
}

//線段樹模版
void build(int l,int r,int root)
{
    memset(tree[root].ex,0,sizeof(tree[root].ex));
    if(l==r){
        ll x;
        scanf("%lld",&x);
        tree[root].ex[l%2][l%2]=1;
        tree[root].val[l%2][l%2]=x;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);
    tree[root]=pushup(tree[root<<1],tree[root<<1|1]);
}

void update(int L,ll C,int l,int r,int root)
{
    if(l==r){
        memset(tree[root].ex,0,sizeof(tree[root].ex));
        tree[root].ex[L%2][L%2]=1;
        tree[root].val[L%2][L%2]=C;
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid)  update(L,C,l,mid,root<<1);
    else update(L,C,mid+1,r,root<<1|1);
    tree[root]=pushup(tree[root<<1],tree[root<<1|1]);
    return;
}

Point query(int L,int R,int l,int r,int root)
{
    if(L<=l&&r<=R){
        return tree[root];
    }
    int mid=(l+r)>>1;
    Point ans1,ans2;
    int fg1=0,fg2=0;
    if(L<=mid){
        ans1=query(L,R,l,mid,root<<1);
        fg1=1;
    }
    if(R>mid){
        ans2=query(L,R,mid+1,r,root<<1|1);
        fg2=1;
    }
    if(fg1==0)  return ans2;
    else if(fg2==0) return ans1;
    else    return pushup(ans1,ans2);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&m);
        build(1,n,1);
        while(m--){
            ll op,x,y;
            scanf("%lld%lld%lld",&op,&x,&y);
            if(op==0){
                Point ans=query(x,y,1,n,1);
                ll Max;
                int fg=0;
                //ans為4中情況的最大值
                for(int i=0;i<2;i++){
                    for(int j=0;j<2;j++){
                        if(ans.ex[i][j]){
                            if(fg==0){
                                Max=ans.val[i][j];
                                fg=1;
                            }
                            else
                                Max=max(Max,ans.val[i][j]);
                        }
                    }
                }
                printf("%lld\n",Max);
            }
            else if(op==1){
                update(x,y,1,n,1);
            }
        }
    }
    return 0;
}