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],…,fn
第三行包含m个整数w[1],w[2],…,wm
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。

HINT


传送门
不会做orz……
对于某一个数字x,在它只出现一次的区间内相当于都增加了w[x](权值)
记x之前的位置是pre[x],那么遇到一个x,
就在(pre[x],x]的区间都加上w[x],并且在(pre[pre[x]],pre[x]]的区间减去w[x],。
每次在[1,x]查询一下某一位置的最大值。
线段树维护就好了……
好巧啊。。完全没想到= =

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int 
    N=1000005;
int n,m,f[N],pre[N],TMP[N];
ll w[N];
struct Segment{ll MAX,mark;}seg[N<<2];
void up(int u){
    int l=u<<1,r=u<<1|1;
    seg[u].MAX=max(seg[l].MAX,seg[r].MAX);
}
void down(int u){
    if (seg[u].mark){
        int l=u<<1,r=u<<1|1;
        seg[l].mark+=seg[u].mark;
        seg[l].MAX+=seg[u].mark;
        seg[r].mark+=seg[u].mark;
        seg[r].MAX+=seg[u].mark;
        seg[u].mark=0LL;
    }
}
void update(int id,int l,int r,int gl,int gr,ll num){
    if (l>=gl && r<=gr){
        seg[id].mark+=num;
        seg[id].MAX+=num;
        return;
    }
    down(id);
    int mid=(l+r)>>1;
    if (gl<=mid) update(id<<1,l,mid,gl,gr,num);
    if (gr>mid) update(id<<1|1,mid+1,r,gl,gr,num);
    up(id);
}
ll query(int id,int l,int r,int gl,int gr){
    if (l>=gl && r<=gr) return seg[id].MAX;
    down(id);
    int mid=(l+r)>>1;ll t=0LL;
    if (gl<=mid) t=max(t,query(id<<1,l,mid,gl,gr));
    if (gr>mid) t=max(t,query(id<<1|1,mid+1,r,gl,gr));
    return t;
}
int main(){
    scanf("%d%d",&n,&m);
    memset(TMP,0,sizeof(TMP));
    for (int i=1;i<=n;i++){
        scanf("%d",&f[i]);
        pre[i]=TMP[f[i]];
        TMP[f[i]]=i;
    }
    pre[0]=-1;
    for (int i=1;i<=m;i++) scanf("%lld",&w[i]);
    ll ans=0LL;
    for (int i=1;i<=n;i++){
        update(1,1,n,pre[i]+1,i,w[f[i]]);
        if (pre[i])
            update(1,1,n,pre[pre[i]]+1,pre[i],-w[f[i]]);
        ans=max(ans,query(1,1,n,1,i));
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值