BZOJ 3747: [POI2015]Kinoman(線段樹)
阿新 • • 發佈:2019-01-13
解題思路
遇到這種選區間的題,很多時候都是列舉左端點,然後計算右端點。考慮列舉左端點之後如何維護,設下一部和\(i\)這個位置的電影相同的為\(nxt[i]\),那麼如果左端點從\(i\)移動到\(i+1\),\([i+1,nxt[i]-1]\)這段區間會減少\(w[f[i]]\),而\([nxt[i],nxt[nxt[i]]-1\)會增加\(w[f[i]]\),發現可以線段樹維護。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> using namespace std; const int N=1000005; typedef long long LL; inline int rd(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return f?x:-x; } inline LL max(LL x,LL y){return x>y?x:y;} int n,m,pre[N],w[N],nxt[N],cnt[N],f[N]; LL ans,Max[N<<2],tag[N<<2]; inline void pushdown(int x){ Max[x<<1]+=tag[x];Max[x<<1|1]+=tag[x]; tag[x<<1]+=tag[x];tag[x<<1|1]+=tag[x]; tag[x]=0; } void update(int x,int l,int r,int L,int R,LL k){ if(L>R) return ; if(L<=l && r<=R) {Max[x]+=k;tag[x]+=k;return ;} int mid=(l+r)>>1;if(tag[x]) pushdown(x); if(L<=mid) update(x<<1,l,mid,L,R,k); if(mid<R) update(x<<1|1,mid+1,r,L,R,k); Max[x]=max(Max[x<<1],Max[x<<1|1]); } LL query(int x,int l,int r,int L,int R){ if(L<=l && r<=R) return Max[x]; int mid=(l+r)>>1;if(tag[x]) pushdown(x); if(L>mid) return query(x<<1|1,mid+1,r,L,R); else if(R<=mid) return query(x<<1,l,mid,L,R); else return max(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R)); } signed main(){ n=rd(),m=rd();int x; for(int i=1;i<=n;i++){ x=rd();f[i]=x; if(pre[x]) nxt[pre[x]]=i; pre[x]=i; } LL now=0; for(int i=1;i<=m;i++) w[i]=rd(); for(int i=1;i<=n;i++){ if(!cnt[f[i]]) now+=w[f[i]]; else if(cnt[f[i]]==1) now-=w[f[i]]; cnt[f[i]]++;update(1,1,n,i,i,now); } for(int i=1;i<=n;i++){ ans=max(ans,query(1,1,n,i,n)); if(!nxt[i]) update(1,1,n,i+1,n,-w[f[i]]); else update(1,1,n,i+1,nxt[i]-1,-w[f[i]]); if(nxt[i] && !nxt[nxt[i]]) update(1,1,n,nxt[i],n,w[f[i]]); else update(1,1,n,nxt[i],nxt[nxt[i]]-1,w[f[i]]); } printf("%lld\n",ans); return 0; }