• 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<=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。


    线段树可以维护的真的不只是线段,区间信息什么的,或者时间以及各种维度

    只有你想不到没有线段树维护不了的

    这道题就是线段树小技巧的运用,自己也是看了题解才做出来的

    我们先考虑全局状态,假设当前我们知道一个区间[l,r]的答案,那么我们考虑怎么把l向右移动一位,也就是在区间中减少一次f[l]的出现次数
    如果我们对于一个l,同时维护它对应的所有的r的答案呢?
    我们发现,答案的变化之和下一个f[l]的位置有关
    所以我们可以预处理一下nxt[l]表示下一个颜色和l相同的位置
    那么我们可以发现,删掉一个l之后l nxt[l]1的答案增加了w[l]
    nxt[l] nxt[nxt[l]]的答案增加了w[l]

    当然还需要判断一下边界什么的

    细节最好自己思考清楚


    #include<bits/stdc++.h>
    using namespace std;
    #define LD (t<<1)
    #define RD (t<<1|1)
    #define N 1000010
    #define LL long long
    LL maxv[N<<2],add[N<<2];
    int n,m,f[N],w[N],nxt[N],last[N],vl[N];
    void pushdown(int t){
        if(add[t]){
            add[LD]+=add[t];maxv[LD]+=add[t];
            add[RD]+=add[t];maxv[RD]+=add[t];
            add[t]=0;
        }
    }
    void pushup(int t){maxv[t]=max(maxv[LD],maxv[RD]);}
    void build(int t,int l,int r){
        if(l>=r)return;
        int mid=(l+r)>>1;
        build(LD,l,mid);
        build(RD,mid+1,r);
        pushup(t);
    }
    void modify(int t,int l,int r,int L,int R,int val){
        if(l>r)return;
        if(L<=l&&r<=R){add[t]+=val;maxv[t]+=val;return;}
        pushdown(t);
        int mid=(l+r)>>1;
        if(R<=mid)modify(LD,l,mid,L,R,val);
        else if(L>mid)modify(RD,mid+1,r,L,R,val);
        else{
            modify(LD,l,mid,L,mid,val);
            modify(RD,mid+1,r,mid+1,R,val);
        }
        pushup(t);
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&f[i]);
        for(int i=1;i<=m;i++)scanf("%d",&w[i]);
        for(int i=n;i>=1;i--)nxt[i]=last[f[i]],last[f[i]]=i;
        for(int i=1;i<=m;i++)
            if(last[i]){
                if(!nxt[last[i]])modify(1,1,n,last[i],n,w[i]);
                else modify(1,1,n,last[i],nxt[last[i]]-1,w[i]);
            }
        LL ans=0;
        for(int i=1;i<=n;i++){
            ans=max(ans,maxv[1]);
            if(!nxt[i])modify(1,1,n,i,n,-w[f[i]]);
            else{
                modify(1,1,n,i,nxt[i]-1,-w[f[i]]);
                if(nxt[nxt[i]])modify(1,1,n,nxt[i],nxt[nxt[i]]-1,w[f[i]]);
                else modify(1,1,n,nxt[i],n,w[f[i]]);
            }
        }
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    【线性表2】线性表的顺序实现:顺序表
    【JSP】EL函数和自定义EL函数
    移动架构-策略模式
    移动架构-状态模式
    移动架构-观察者模式
    移动架构-模板模式
    移动架构-解释器模式
    移动架构-命令模式
    移动架构-责任链模式
    移动架构之建造者模式
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676292.html
Copyright © 2020-2023  润新知