Magician(hdu 5316 線段樹區間合併)
阿新 • • 發佈:2018-11-25
題目連結:
題意:
給定一個數組,定義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; }