码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ-3747】Kinoman 线段树

时间:2016-10-03 10:40:49      阅读:128      评论:0      收藏:0      [点我收藏+]

标签:

3747: [POI2015]Kinoman

Time Limit: 60 Sec  Memory Limit: 128 MB
Submit: 715  Solved: 294
[Submit][Status][Discuss]

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。

HINT

Source

鸣谢Jcvb

Solution

经典题,然而想了一会.

我们先预处理出$suf[i]$表示$i$这个位置放的电影,和它同样的电影下一次放的位置。

然后我们枚举左端点,不断更新答案就行。

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<0 || ch>9) {if (ch==-) f=-1; ch=getchar();}
    while (ch>=0 && ch<=9) {x=x*10+ch-0; ch=getchar();}
    return x*f; 
}
#define MAXN 1000100
int N,M;
namespace SegmentTree
{
    struct SegmentTreeNode{int l,r; LL tag,maxx;}tree[MAXN<<2];
    #define ls now<<1
    #define rs now<<1|1
    inline void Update(int now) {tree[now].maxx=max(tree[ls].maxx,tree[rs].maxx);}
    inline void PushDown(int now)
    {
        if (tree[now].l==tree[now].r || !tree[now].tag) return;
        LL D=tree[now].tag; tree[now].tag=0;
        tree[ls].maxx+=D; tree[ls].tag+=D;
        tree[rs].maxx+=D; tree[rs].tag+=D;
    }
    inline void BuildTree(int now,int l,int r)
    {
        tree[now].l=l; tree[now].r=r;
        if (l==r) return;
        int mid=(l+r)>>1;
        BuildTree(ls,l,mid); BuildTree(rs,mid+1,r);
        Update(now);
    }
    inline void Modify(int now,int L,int R,int D)
    {
        if (L>R) return;
        int l=tree[now].l,r=tree[now].r;
        PushDown(now);
        if (L<=l && R>=r) {tree[now].maxx+=D; tree[now].tag+=D; return;}
        int mid=(l+r)>>1;
        if (L<=mid) Modify(ls,L,R,D);
        if (R>mid) Modify(rs,L,R,D);
        Update(now);
    }
}dddddd
int suf[MAXN],last[MAXN],f[MAXN],w[MAXN],first[MAXN];
LL ans;
int main()
{
    N=read(),M=read();
    for (int i=1; i<=N; i++) f[i]=read();
    for (int i=1; i<=M; i++) w[i]=read();
    for (int i=1; i<=N; i++)
        suf[last[f[i]]]=i,last[f[i]]=i;
    for (int i=1; i<=N; i++) 
        if (!first[f[i]]) first[f[i]]=i;
    SegmentTree::BuildTree(1,1,N);
    for (int i=1; i<=M; i++)
        if (first[i])
            SegmentTree::Modify(1,first[i],suf[first[i]]? suf[first[i]]-1:N,w[i]);
    for (int i=1; i<=N; i++)
        {
            ans=max(ans,SegmentTree::tree[1].maxx);
            SegmentTree::Modify(1,i,suf[i]? suf[i]-1:N,-w[f[i]]);
            if (suf[i]) SegmentTree::Modify(1,suf[i],suf[suf[i]]? suf[suf[i]]-1:N,w[f[i]]); else continue;
        }
    printf("%lld\n",ans);
    return 0;
}

经典题我居然现在才做....

【BZOJ-3747】Kinoman 线段树

标签:

原文地址:http://www.cnblogs.com/DaD3zZ-Beyonder/p/5928802.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!