3747 [POI2015]Kinoman

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

Sample Output

15
样例解释:
观看第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;
 }
 
 
 
 
原文地址:https://www.cnblogs.com/HJWJBSR/p/4149913.html