牛客 Optimal Milking 題解(線段樹維護區間合併)

題目鏈接

題目大意

給出一個n(n≤4e4)個數的數列{an}(ai≥1)。一個數列的最大貢獻定義爲其中若干個不相鄰的數的和的最大值。進行m(m≤5e4)次操作,每次修改數列中的一個數並詢問此時的最大貢獻。

題目思路

看到題目,如果這個題不修改,那麼就是一個簡單的dp,然而他要修改,所以我只會暴力的o(n*m)
題解看的我生活不能自理,居然是線段樹維護區間合併。。可能是我以前寫的線段樹都太簡單了吧。不過其實也容易想到線段樹,因爲是單點修改,看他的數據也挺符合線段樹的操作的,第一次寫此類題,值得學習

線段樹。
對於線段樹上每個節點[L,R],維護四個值f00,f01,f10,f11,分別表示aL,aR都不選,不選aL選aR,選aL不選aR,aL,aR都選的最大貢獻。那麼ans=max{f[rt]}。
接下來只需要考慮如何合併。其實很簡單,只要保證中間的兩個不全是1就好:
f00=max{f00[Ls]+f00[Rs],f01[Ls]+f00[Rs],f00[Ls]+f10[Rs]}
f01=max{f00[Ls]+f01[Rs],f01[Ls]+f01[Rs],f00[Ls]+f11[Rs]}
f10=max{f10[Ls]+f00[Rs],f11[Ls]+f00[Rs],f10[Ls]+f10[Rs]}
f11=max{f10[Ls]+f01[Rs],f11[Ls]+f01[Rs],f10[Ls]+f11[Rs]}
時間複雜度O(mlogn)。

代碼

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=4e4+5;
int n,d,tree[maxn<<2][4];
ll ans;
//0 代表左右都不選,1代表右邊不選,2代表左邊不選,3代表兩邊都選
void push(int node){
    tree[node][0]=max(tree[node<<1][0]+tree[node<<1|1][0],max(tree[node<<1][0]+tree[node<<1|1][1],tree[node<<1][2]+tree[node<<1|1][0]));
    tree[node][1]=max(tree[node<<1][1]+tree[node<<1|1][0],max(tree[node<<1][1]+tree[node<<1|1][1],tree[node<<1][3]+tree[node<<1|1][0]));
    tree[node][2]=max(tree[node<<1][0]+tree[node<<1|1][2],max(tree[node<<1][2]+tree[node<<1|1][2],tree[node<<1][0]+tree[node<<1|1][3]));
    tree[node][3]=max(tree[node<<1][1]+tree[node<<1|1][2],max(tree[node<<1][3]+tree[node<<1|1][2],tree[node<<1][1]+tree[node<<1|1][3]));
}
void build(int node,int l,int r){
    if(l==r){
        scanf("%d",&tree[node][3]);
        return ;
    }
    int mid=(l+r)/2;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
    push(node);
}
void update(int node,int pos,int l,int r,int num){
    if(l==r){
        tree[node][3]=num;
        return ;
    }
    int mid=(l+r)/2;
    if(mid>=pos)    update(node<<1,pos,l,mid,num);
    else            update(node<<1|1,pos,mid+1,r,num);
    push(node);
}
int main(){
    scanf("%d %d",&n,&d);
    build(1,1,n);
    for(int i=1,pos,num;i<=d;i++){
        scanf("%d %d",&pos,&num);
        update(1,pos,1,n,num);
        ans+=max(max(tree[1][0],tree[1][1]),max(tree[1][2],tree[1][3]));
    }
    printf("%lld\n",ans);
    return 0;
}

參考鏈接:https://www.cnblogs.com/VisJiao/p/LgP3097.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章