bzoj3747 [POI2015]Kinoman
线段树,记录next[i]下一部与当前电影一样的位置,然后枚举区间左端点i,询问线段树最大值后删除i到next[i-1]这段区间的观影值,且增加next[i]到next[next[i]]-1这段区间的观影值。
代码,跑的有点慢
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int N = 5001010; 5 const int M = 2000010; 6 int pos[M],a[M],b[M]; 7 int n,m,i,next[M]; 8 long long ans,s[N],v[N]; 9 void clean(int x) 10 { 11 if (v[x]) 12 { 13 s[x]+=v[x]; 14 v[2*x]+=v[x]; 15 v[2*x+1]+=v[x]; 16 v[x]=0; 17 } 18 } 19 void change(int x,int l,int r,int a,int b,int c) 20 { 21 clean(x); 22 if ((a<=l)&&(r<=b)) 23 { 24 v[x]+=c; 25 return; 26 } 27 int m=(l+r)>>1; 28 if (a<m) change(2*x,l,m,a,b,c); 29 if (m<b) change(2*x+1,m,r,a,b,c); 30 clean(2*x);clean(2*x+1); 31 s[x]=max(s[2*x],s[2*x+1]); 32 } 33 int main() 34 { 35 scanf("%d%d",&n,&m); 36 for (i=1;i<=n;i++) 37 scanf("%d",&a[i]); 38 for (i=1;i<=m;i++) 39 scanf("%d",&b[i]); 40 for (i=1;i<=n;i++) 41 { 42 if (pos[a[i]]) next[pos[a[i]]]=i; 43 pos[a[i]]=i; 44 } 45 for (i=1;i<=n;i++) if (next[i]==0) next[i]=n+1; 46 47 for (i=1;i<=m;i++) pos[i]=0; 48 for (i=1;i<=n;i++) 49 { 50 if (pos[a[i]]==0) change(1,0,n,i-1,next[i]-1,b[a[i]]); 51 pos[a[i]]=1; 52 } 53 54 for (i=1;i<=n;i++) 55 { 56 clean(1);ans=max(ans,s[1]); 57 change(1,0,n,i-1,next[i]-1,-b[a[i]]); 58 if (next[i]!=n+1) 59 change(1,0,n,next[i]-1,next[next[i]]-1,b[a[next[i]]]); 60 } 61 printf("%lld\n",ans); 62 }