BZOJ 3747 [POI2015]Kinoman

39 篇文章 0 订阅
7 篇文章 0 订阅

题目在这里呀!

题意:

m部电影,n天放映,第i天放映第f[i]部电影,第i部电影的好看值为w[i]。
一个区间[l,r],在第l天到第r天内,如果第i部电影只被看过一遍,那么就有w[i]的贡献,求最大贡献。

题解:

感觉是一道好题哦~(5月份没更过博啊终于打算写几篇了qwq)

从暴力入手吧,枚举左端点l,向右扫,每次cnt[f[i]]++,如果此时cnt[f[i]]=1,则加上贡献,如果cnt[f[i]]=2,则减去当前贡献,去最大值。

那么怎么去优化它呢。我们可以考虑当左端点从l移动到l+1的时候,改变的贡献。所以用tree[i]记录l到i这段区间内的贡献,放入线段树中求最大值,现在就是要考虑如何修改了,我们用nxt[i]记录从第i天往下下一个f[i]是第几天。
所以当左端点移动时,cnt[f[l]]–,那么tree[l+1…nxt[l]-1]-=w[f[l]],tree[nxt[l]…nxt[nxt[l]]-1]+=w[f[l]],在线段树上区间修改即可。
好像没什么要多注意的?(我打这道题还算挺顺利的吧

//Suplex
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1001000
using namespace std;
int n,m,f[N],w[N],nxt[N],fr[N];
long long ans;

struct Segment{
    long long tag,val;
}t[N+N+N+N+N];

inline void pushdown(int p,int l,int r)
{
    if(l==r) return;
    t[p+p].tag+=t[p].tag;t[p+p+1].tag+=t[p].tag;
    t[p+p].val+=t[p].tag;t[p+p+1].val+=t[p].tag;
    t[p].tag=0;
}

void modify(int p,int l,int r,int x,int y,int delta)
{
    if(t[p].tag) pushdown(p,l,r);
    if(x<=l && r<=y){
        t[p].tag=delta;t[p].val+=delta;
        return;
    }
    int mid=(l+r)>>1;
    if(y<=mid) modify(p+p,l,mid,x,y,delta);
    else if(x>mid) modify(p+p+1,mid+1,r,x,y,delta);
    else modify(p+p,l,mid,x,mid,delta),modify(p+p+1,mid+1,r,mid+1,y,delta);
    t[p].val=max(t[p+p].val,t[p+p+1].val);
}

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;i--){
        nxt[i]=fr[f[i]];
        fr[f[i]]=i;
    }
    for(int i=1;i<=m;i++)
         if(fr[i]){
             if(!nxt[fr[i]]) modify(1,1,n,fr[i],n,w[i]);
             else modify(1,1,n,fr[i],nxt[fr[i]]-1,w[i]);
         }
    for(int l=1;l<=n;l++){
        ans=max(ans,t[1].val);
        if(nxt[l]){
            modify(1,1,n,l,nxt[l]-1,-w[f[l]]);     
            if(nxt[nxt[l]]) modify(1,1,n,nxt[l],nxt[nxt[l]]-1,w[f[l]]);
            else modify(1,1,n,nxt[l],n,w[f[l]]);
        }
        else modify(1,1,n,l,n,-w[f[l]]);
    }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值