3747: [POI2015]Kinoman
Time Limit: 30 Sec Memory Limit: 128 MB
Description
共有m部电影,编号为1~m,第i部电影的好看值为w[i]。
在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。
你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。
Input
第一行两个整数n,m(1<=m<=n<=1000000)。
第二行包含n个整数f[1],f[2],…,f[n](1<=f[i]<=m)。
第三行包含m个整数w[1],w[2],…,w[m](1<=w[j]<=1000000)。
Output
输出观看且仅观看过一次的电影的好看值的总和的最大值。
Sample Input
9 4
2 3 1 1 4 1 2 4 1
5 3 6 6
2 3 1 1 4 1 2 4 1
5 3 6 6
Sample Output
15
样例解释:
观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。
样例解释:
观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。
这是Gromah学长给我的线段树操作第二道练习题。
思路:
……一片混乱
我开始先处理出a[i]代表看从1到i的电影的好看值,Ne[i]代表f[i]出现的下一个位置。
然后将第1天的电影去掉之后,影响是第2天看到第i天(i取值从2到Ne[1]-1)都会好看值减去w[f[1]](即a[i]-=w[f[1]]),从第2天看到第i天(Ne[i]到Ne[Ne[i]]-1)都会好看值加上w[f[1]]。
那么思路就出来了,只要从第1天处理到第n-1天,每次把当天删去,然后区间加法(线段树维护),对于每次处理后,都要查询最大值(另一个线段树维护)。
24 Sec过了。
(忽然想起UOJ上有人贴的一张图,刷一道时限为60Sec的题。这人竟然没被封号也是奇迹!)
不说闲话,贴代码:
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; #define N 1050000 int n,m,Ne[N],La[N],w[N],f[N]; long long maxx=0,Ma[2*N],Zs[2*N]; bool b[N]; long long Max(long long x,long long y) { if (x>y) return x;else return y; } void Plin(int x,int y,int z,long long v,int t) { int i=(x+y)/2; if (x==y) { Zs[z]=v; return; } if (t>i) Plin(i+1,y,z*2+1,v,t);else Plin(x,i,z*2,v,t); Zs[z]=Max(v,Zs[z]); return; } void Insert(int x,int y,int z,int s,int t,int v) { int i=(z+s)/2; if (z==s) { Zs[t]+=v; return; } if (x==z&&y==s) { Ma[t]+=v; Zs[t]+=v; return; } if (x<=i) Insert(x,min(i,y),z,i,t*2,v); if (y>i) Insert(max(i+1,x),y,i+1,s,t*2+1,v); Zs[t]=Max(Zs[t*2],Zs[t*2+1])+Ma[t]; return; } void Did(int x,int y,int z,int s,int t,int v) { if (x<=y) Insert(x,y,z,s,t,v); return; } int main() { int i,j,k,l,q; long long e; memset(Ma,0,sizeof(Ma));memset(La,0,sizeof(La)); memset(b,0,sizeof(b));memset(w,0,sizeof(w)); memset(f,0,sizeof(f));memset(Ne,0,sizeof(Ne)); memset(Zs,0,sizeof(Zs)); scanf("%d%d",&n,&m); for (i=1;i<=n;i++) scanf("%d",&f[i]); for (i=1;i<=m;i++) scanf("%d",&w[i]); e=0; for (i=1;i<=n;i++) { if (La[f[i]]==0) e+=w[f[i]];else { Ne[La[f[i]]]=i; if (!b[f[i]]) { e-=w[f[i]]; b[f[i]]=true; } } Plin(1,n,1,e,i); La[f[i]]=i; } maxx=Zs[1]; for (i=1;i<n;i++) { if (Ne[i]==0) Did(i+1,n,1,n,1,-w[f[i]]);else { Did(i+1,Ne[i]-1,1,n,1,-w[f[i]]); if (Ne[Ne[i]]==0) Did(Ne[i],n,1,n,1,w[f[i]]);else Did(Ne[i],Ne[Ne[i]]-1,1,n,1,w[f[i]]); } maxx=Max(maxx,Zs[1]); } printf("%lld ",maxx); return 0; }