【BZOJ-3747】Kinoman 线段树

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;
}

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

时间: 2024-08-11 03:36:30

【BZOJ-3747】Kinoman 线段树的相关文章

BZOJ 3747: [POI2015]Kinoman( 线段树 )

线段树... 我们可以枚举左端点 , 然后用线段树找出所有右端点中的最大值 . ----------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( i

BZOJ 3747 POI 2015 Kinoman 线段树

题目大意:给出电影院的放映电影顺序,一个电影只有看过一次的时候会获得电影的权值.没看过或者看两次或以上都不能获得权值.问看连续区间的电影能够获得的最大权值是多少. 思路:利用线段树维护前缀和.将出现第一次的地方的权值加上那部电影的权值,第二次出现的时候权值减去那部电影的权值.枚举起点,先更新答案,然后在当前节点减去权值的二倍,然后再在下一次出现的地方加上权值(我感觉我没说明白,总之看代码吧... CODE: #include <cstdio> #include <cstring>

3747: [POI2015]Kinoman|线段树

枚举左区间线段树维护最大值 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #include<queue> #include<set> #include<map> #define ll l

【BZOJ 3476】 线段树===

59  懒惰的奶牛贝西所在的牧场,散落着 N 堆牧草,其中第 i 堆牧草在 ( Xi,Yi ) 的位置,数量有 Ai 个单位.贝西从家移动到某一堆牧草的时候,只能沿坐标轴朝正北.正东.正西.正南这四个方向移动,所以计算贝西和牧草间的距离时,应采用"曼哈顿距离"-- (x,y ) 和 (x ′,y ′) 之间的距离为|x ? x ′ | + |y ? y ′ |.例如贝西的家在 (0.5, 0.3),有一堆牧草在 (3, 2),那么它们之间的距离就是 4.2.贝西懒得走动,她想请你为它寻

【BZOJ3747】[POI2015]Kinoman 线段树

[BZOJ3747][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&

bzoj 4627 值域线段树

4627: [BeiJing2016]回转寿司 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 523  Solved: 227[Submit][Status][Discuss] Description 酷爱日料的小Z经常光顾学校东门外的回转寿司店.在这里,一盘盘寿司通过传送带依次呈现在小Z眼前.不同的寿 司带给小Z的味觉感受是不一样的,我们定义小Z对每盘寿司都有一个满意度,例如小Z酷爱三文鱼,他对一盘三文 鱼寿司的满意度为10:小Z觉得金枪鱼没有

bzoj 1095 捉迷藏(线段树)

题外话 最近课程不是很紧,准备按AC率版切bz,争取一天一道题以上.然后我喜闻乐见的发现之前剩下的题基本都是数据结构>_<.蛋疼啊... Description 给定一棵树,每个节点要么是黑色,要么是白色,能执行两个操作:把某一个点取反色,返回距离最远的黑色点对. Solution 这题看起来链分治,边分治都可做,然后搜到了小岛的题解.发现了逼格更高的做法,看了曹钦翔的<数据结构的提炼与压缩>,跪烂了... 这题用到了dfs序的性质,也就是括号序列.. 定义一种对一棵树的括号编码.

BZOJ 1067 降雨量 线段树

传送门 这题也是醉了 估计考点是特判 不是数据结构 考察if和else的运用 主要是考虑几种情况的分类 看代码吧 对了 我写的是线段树 因为我懒 懒得搞ST #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #define N 50000+5 #define M 200000+5 using namespace std; int year[N],a[

BZOJ 1230--lites 开关灯(线段树)

1230: [Usaco2008 Nov]lites 开关灯 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1682  Solved: 876[Submit][Status][Discuss] Description Farmer John尝试通过和奶牛们玩益智玩具来保持他的奶牛们思维敏捷. 其中一个大型玩具是牛栏中的灯. N (2 <= N <= 100,000) 头奶牛中的每一头被连续的编号为1..N, 站在一个彩色的灯下面.刚到傍晚的时候

BZOJ.3585.mex(线段树)

题目链接 考虑\([1,i]\)的\(mex[i]\),显然是单调的 而对于\([l,r]\)与\([l+1,r]\),如果\(nxt[a[l]]>r\),那么\([l+1,r]\)中所有\(>a[l]\)的数显然要改成\(a[l]\) 询问排序,离散化,预处理下nxt[],剩下就是线段树的区间更新.查询了 /* 离散化的时候>=n的全部看做n就好了 查询时是只需查r点的(l之前能更新r的已经更新完了,初始时是[1,r],r点现在就是[l,r]了) 单点即可不需要PushUp(也不好得某