【題目描述】
一個長為n的序列,每個元素有一個a[i],b[i],a[i]為0||1,每個點和他相鄰的兩個點分別有兩條邊,權值為cost1[i],cost2[i],對於每個區間l,r,我們可以給每一個數一個c[i]值,c[i]=0||1,使得Σ(b[j]*(a[j]^c[j]))+cost1[j]*(c[j]^c[j+1])+cost2[j]*(c[j]^c[j+2]),j,j+1,j+2在[l,r]內時累加,現在有m次操作:
M x:將x位置的a[i]^=1;
Q l r:詢問區間l,r的答案。
首先假設詢問的只是一個區間,那么我們可以比較容易的用dp來求解w[i][s]表示到第i位的時候,后兩位選的s的最小值,那么我們可以轉移到w[i+1][ss]。這樣就可以得到答案了。
但是現在我們支持區間詢問和修改,所以我們用線段樹來維護,每個節點記錄w[s],s為這個區間左面兩個點c[i]的選取方法和右面兩個點的選取方法,那么只要我們可以維護線段樹每個節點的w[i],我們就可以解決這個問題了。
對於合並操作比較簡單,只需要枚舉兩個節點的s,然后更新的ss就可以了。
反思:一個節點的情況需要特殊處理,開始沒注意到這個,后來改了半天,合並的時候還分了三種情況討論= =。
還需要開LL,設inf的時候設的是#define inf (1<<60) 結果改了半天= =
評測數據的5號評測點少了一個操作,然后我的輸出就多了一行= =。
//By BLADEVIL #include <cstdio> #include <cstring> #define min(x,y) (x>y)?y:x; #define inf (1ll<<60) #define maxn 30010 #define LL long long using namespace std; struct rec { int left,right; LL key; LL w[20]; rec() { left=right=0; key=inf; for (LL i=0;i<20;i++) w[i]=inf; } }t[maxn<<2]; LL n,m; LL a[maxn],b[maxn],cost1[maxn],cost2[maxn]; char s[10]; void init(int x) { t[x].w[0]=b[t[x].left]*a[t[x].left]; t[x].w[1]=b[t[x].left]*(a[t[x].left]^1); t[x].key=min(t[x].w[0],t[x].w[1]); } rec update(rec a,rec b) { rec ans; ans.left=a.left; ans.right=b.right; if ((a.left==a.right)&&(b.left==b.right)) for (LL s=0;s<4;s++) { LL ss=s|(s<<2); LL c1=((s&2)==2)?1:0,c2=((s&1)==1)?1:0; //if ((ss==3)&&(a.left==1)) printf("%d %d\n",c1,c2); if ((a.w[c1]==inf)||(b.w[c2]==inf)) continue; ans.w[ss]=min(ans.w[ss],a.w[c1]+b.w[c2]+(c1^c2)*cost1[a.right]); //if ((ss==3)&&(a.left==1)&&(a.right==1)) printf("%d\n",ans.w[ss]); ans.key=min(ans.key,ans.w[ss]); //if ((a.left==1)&&(a.right==1)) printf("%d %d\n",ans.key,ss); } else if (a.left==a.right) for (LL s1=0;s1<2;s1++) for (LL s2=0;s2<16;s2++) { if ((a.w[s1]==inf)||(b.w[s2]==inf)) continue; LL s=0; s|=s1<<3; s|=(s2&8)>>1; s|=s2&3; LL c2=((s2&8)==8)?1:0,c3=((s2&4)==4)?1:0; ans.w[s]=min(ans.w[s],a.w[s1]+b.w[s2]+(s1^c2)*cost1[a.left]+(s1^c3)*cost2[a.left]); ans.key=min(ans.key,ans.w[s]); } else if (b.left==b.right) for (LL s1=0;s1<16;s1++) for (LL s2=0;s2<2;s2++) { if ((a.w[s1]==inf)||(b.w[s2]==inf)) continue; LL s=0; s|=s2; s|=(s1&1)<<1; s|=s1&12; LL c1=((s1&2)==2)?1:0,c2=((s1&1)==1)?1:0; ans.w[s]=min(ans.w[s],a.w[s1]+b.w[s2]+(c1^s2)*cost2[a.right-1]+(c2^s2)*cost1[a.right]); ans.key=min(ans.key,ans.w[s]); } else if ((a.left!=a.right)&&(b.left!=b.right)) for (LL s1=0;s1<16;s1++) for (LL s2=0;s2<16;s2++) { if ((a.w[s1]==inf)||(b.w[s2])==inf) continue; LL s=(s1>>2)<<2; s|=s2&3; LL c1=((s1&2)==2)?1:0,c2=((s1&1)==1)?1:0; LL c3=((s2&8)==8)?1:0,c4=((s2&4)==4)?1:0; LL Ans=a.w[s1]+b.w[s2]; Ans+=cost1[a.right]*(c2^c3)+cost2[a.right]*(c2^c4)+cost2[a.right-1]*(c1^c3); ans.w[s]=min(ans.w[s],Ans); ans.key=min(ans.key,ans.w[s]); } return ans; } void build(int x,int l,int r) { t[x].left=l; t[x].right=r; if (t[x].left==t[x].right) { init(x); return ; } int mid=l+r>>1; build(x<<1,l,mid); build((x<<1)+1,mid+1,r); t[x]=update(t[x<<1],t[(x<<1)+1]); } rec query(int x,int l,int r) { if ((t[x].left==l)&&(t[x].right==r)) return t[x]; int mid=t[x].left+t[x].right>>1; if (mid>=r) return query(x<<1,l,r); else if (mid<l) return query((x<<1)+1,l,r); else return update(query(x<<1,l,mid),query((x<<1)+1,mid+1,r)); } void change(int x,int y) { if (t[x].left==t[x].right) { init(x); return ; } LL mid=t[x].left+t[x].right>>1; if (y>mid) change((x<<1)+1,y); else change(x<<1,y); t[x]=update(t[x<<1],t[(x<<1)+1]); } int main() { freopen("game.in","r",stdin); freopen("game.out","w",stdout); scanf("%lld%lld",&n,&m); for (LL i=1;i<=n;i++) scanf("%lld",&a[i]); for (LL i=1;i<=n;i++) scanf("%lld",&b[i]); for (LL i=1;i<n;i++) scanf("%lld",&cost1[i]); for (LL i=1;i<n-1;i++) scanf("%lld",&cost2[i]); build(1,1,n); /* for (LL s=0;s<16;s++) printf("%d %d\n",s,query(1,1,3).w[s]); return 0; */ //printf("%d\n",query(1,2,4).key); return 0; while (m--) { scanf("%s",s); LL x,y; if (s[0]=='Q') { scanf("%lld%lld",&x,&y); printf("%lld\n",query(1,x,y).key); } else { scanf("%lld",&x); a[x]^=1; change(1,x); } } //for (LL s=0;s<16;s++) printf("%d %d\n",s,query(1,1,4).w[s]); fclose(stdin); fclose(stdout); return 0; }