1. 程式人生 > >【bzoj3747】Kinoman[POI2015](線段樹)

【bzoj3747】Kinoman[POI2015](線段樹)

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)
#define
inf 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

【bzoj3747】Kinoman[POI2015](線段樹)