【bzoj3747】Kinoman[POI2015](線段樹)
阿新 • • 發佈:2018-01-18
def online else sin time line code 最大的 splay
題目傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=3747
對於這種題,考慮固定區間的右端點為r,設區間左端點為l能取得的好看值總和為a[l],那麽就相當於當r取不同取值時所有al的最大值。
設last[i]表示第i部電影上一次出現的位置,當右端點r右移1位時,因為只有看了一遍的電影能獲取好看值,所以能取得f[r]的好看值的al只能是在last[r]~r這個區間。因此每次右移時,last[last[r]]+1~last[r]減去w[f[r]],last[r]+1~r加上w[f[r]]。
具體實現, 就是維護一個資瓷區間加與查詢區間最大的線段樹。
代碼(常數真大):
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<ctime> #include<algorithm> #include<vector> #include<queue> #include<map> #define ll long long #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #definebzoj3747inf 1ll<<60 ll read() { ll tmp=0; char c=getchar(),f=1; for(;c<‘0‘||‘9‘<c;c=getchar())if(c==‘-‘)f=-1; for(;‘0‘<=c&&c<=‘9‘;c=getchar())tmp=tmp*10+c-‘0‘; return tmp*f; } using namespace std; struct point{ ll delta,mx; }a[4000010]; ll w[1000010]; int f[1000010],last[1000010],pos[1000010]; int n,m; void add(int now,int l,int r,int x,int y,ll k) { if(x<=l&&r<=y){ a[now].delta+=k; a[now].mx+=k; } else{ int mid=(l+r)>>1; if(x<=mid)add(now<<1,l,mid,x,y,k); if(mid<y)add(now<<1|1,mid+1,r,x,y,k); a[now].mx=max(a[now<<1].mx,a[now<<1|1].mx)+a[now].delta; } } ll getmax(int now,int l,int r,int x,int y) { if(x<=l&&r<=y)return a[now].mx; else{ ll tmp=-inf; int mid=(l+r)>>1; if(x<=mid)tmp=max(tmp,getmax(now<<1,l,mid,x,y)); if(mid<y)tmp=max(tmp,getmax(now<<1|1,mid+1,r,x,y)); return tmp+a[now].delta; } } int main() { int i; n=read(); m=read(); last[0]=-1; for(i=1;i<=n;i++){ f[i]=read(); if(!pos[f[i]])last[i]=0; else last[i]=pos[f[i]]; pos[f[i]]=i; } for(i=1;i<=m;i++)w[i]=read(); ll ans=-inf; for(i=1;i<=n;i++){ add(1,1,n,last[i]+1,i,w[f[i]]); if(~last[last[i]])add(1,1,n,last[last[i]]+1,last[i],-w[f[i]]); ans=max(ans,getmax(1,1,n,1,i)); } printf("%lld\n",ans); }
【bzoj3747】Kinoman[POI2015](線段樹)