首页 > 代码库 > 【BZOJ-3747】Kinoman 线段树

【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 longinline 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 1000100int 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);    }}ddddddint 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 线段树