bzoj 3747: [POI2015]Kinoman

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

Sample Output

15
样例解释:

观看第2,3,4,5,6,7天内放映的电影,其中看且仅看过一次的电影的编号为2,3,4。


线段树的一眼题。。

枚举右端点。然后维护每个点为左端点的ans

设pre[i]为f[i]上一次出现的位置

则每次枚举一个新的右端点就在[pre[i]+1,i]加w[f[i]]然后[pre[pre[i]]+1,pre[i]]减w[f[i]]就行了

线段树区间修改区间查询 维护区间最大值

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
struct tree
{
     long long m;
     long long tag;
}tr[4000001];
inline void build(int p,int l,int r)
{
     if(l!=r)
     {
          int mid=(l+r)/2;
          build(p*2,l,mid);
          build(p*2+1,mid+1,r);
     }
}
inline void push(int p)
{
	 if(p>2000000)
	      return ;
     tr[p*2].m+=tr[p].tag;
     tr[p*2+1].m+=tr[p].tag;
     tr[p*2].tag+=tr[p].tag;
     tr[p*2+1].tag+=tr[p].tag;
     tr[p].tag=0;
}
inline void up(int p)
{
     tr[p].m=max(tr[p*2].m,tr[p*2+1].m);
}
inline void inc(int p,int ll,int rr,int l,int r,long long x)
{
	 if(l>r)
	      return ;
     if(l<=ll&&rr<=r)
     {
          tr[p].m+=x;
          tr[p].tag+=x;
     }
     else
     {
          push(p);
          int mid=(ll+rr)/2;
          if(l<=mid)
               inc(p*2,ll,mid,l,r,x);
          if(r>mid)
               inc(p*2+1,mid+1,rr,l,r,x);
          up(p);
     }
}
inline long long ask(int p,int ll,int rr,int l,int r)
{
     if(l<=ll&&rr<=r)
          return tr[p].m;
     else
     {
          push(p);
          int mid=(ll+rr)/2;
          long long ans=0;
          if(l<=mid)
               ans=max(ans,ask(p*2,ll,mid,l,r));
          if(r>mid)
               ans=max(ans,ask(p*2+1,mid+1,rr,l,r));
          return ans;
     }
}
int f[1000001],la[1000001],pre[1000001];
long long w[1000001];
int main()
{
 //    freopen("data.in","r",stdin);
 //    freopen("data.out","w",stdout);
     int n,m;
     scanf("%d%d",&n,&m);
     int i;
     for(i=1;i<=n;i++)
          scanf("%d",&f[i]);
     for(i=1;i<=m;i++)
          scanf("%lld",&w[i]);
     for(i=1;i<=n;i++)
     {
          pre[i]=la[f[i]];
          la[f[i]]=i;
     }
     build(1,1,n);
     long long ans=0;
     int j;
     for(i=1;i<=n;i++)
     {
          inc(1,1,n,pre[i]+1,i,w[f[i]]);
          inc(1,1,n,pre[pre[i]]+1,pre[i],-w[f[i]]);
          ans=max(ask(1,1,n,1,i),ans);
          /*for(j=1;j<=n;j++)
               printf("%lld ",ask(1,j,j));
          printf("\n");*/
     }
     printf("%lld\n",ans);
     return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值