[COCI2011-2012#2] RASPORED (NW)

5 篇文章 0 订阅

[COCI2011-2012#2] RASPORED

也许是虚标,但更希望是我变强了,做完之后才发现是紫题。
感觉并没有想象中的复杂和难解。


题目描述

Mirko 的比萨店是城里最好的,镇上所有的居民每天午餐都吃比萨饼。而且 Mirko 的送货服务很快,送货时间可以忽略不计。但是 Mirko 只有一个小烤箱,一次只能烤一个比萨饼。

我们将城里的 N N N 个居民从 1 1 1 N N N 编号,他们计划吃午餐的时间为 L i L_i Li,Mirko 需要为他们烘焙比萨的所需时间为 T i T_i Ti

如果一个居民在他计划吃午餐时间的前 K K K 个时间单位收到了他的比萨饼,那么 Mirko 会得到 K K K 元小费。相应地,如果一个居民在他计划吃午餐时间的后 K K K 个时间单位才收到了他的比萨饼,那么 Mirko 必须向居民付款 K K K 元。如果比萨饼准时送到,Mirko 不会得到小费,但是也不用付任何费用。

请你帮助 Mirko 安排一天的比萨烘焙顺序,使得他一天赚取的总小费最大

注意:

  1. 一天从时间单位 0 0 0 开始,你可以认为这一天是无限长的。

  2. 居民们有时会改变他们的 T i , L i T_i,L_i Ti,Li

所有数不超过2e5。


类似这样的题目,如贪心,dp,大抵可以靠数学推式子找到突破口。
当然本题也可以用邻项交换法找到贪心突破口,考场上我也确实是这样做的,不过最终仍然会导回数学推式子的路径来。

对n个订单得到一个排列p后,不难列出式子:

A n s = ∑ i = 1 n ( T p i − ∑ j = 1 i L p j ) Ans=\sum_{i=1}^{n}(T_{p_{i}}-\sum_{j=1}^{i}L_{p_{j}}) Ans=i=1n(Tpij=1iLpj)

先别急着换掉 ∑ L \sum L L,稍微化简一下,得

A n s = ∑ i = 1 n T p i − ∑ i = 1 n ∑ j = 1 i L p j Ans=\sum_{i=1}^{n}T_{p_{i}}-\sum_{i=1}^{n}\sum_{j=1}^{i}L_{p_{j}} Ans=i=1nTpii=1nj=1iLpj

= ∑ i = 1 n T p i − ∑ i = 1 n ( n − i + 1 ) × L p i =\sum_{i=1}^{n}T_{p_{i}}-\sum_{i=1}^{n}(n-i+1)\times L_{p_i} =i=1nTpii=1n(ni+1)×Lpi

这时候已经可以做了,也可以无脑打开:

A n s = ∑ i = 1 n T p i − ( n + 1 ) ∑ i = 1 n L p i + ∑ i = 1 n i ⋅ L p i Ans=\sum_{i=1}^{n}T_{p_{i}}-(n+1)\sum_{i=1}^{n}L_{p_{i}}+\sum_{i=1}^{n}i\cdot L_{p_{i}} Ans=i=1nTpi(n+1)i=1nLpi+i=1niLpi

显然前两项与排列p无关,O(1) 维护即可。显然当 L i L_i Li 从小到大排列时对答案贡献最大。

这是一个经典的维护,首先当然不能维护序列(复杂度过大),考虑单点修改时对答案更新的贡献,发现只与两个端点及其区间 L i L_i Li 的和有关,然后就很清晰,两个端点 O(1) 处理,区间和用树状数组差分即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+5;
int n,m,t[N],l[N],ml[N];
LL st,tr[2][N],sl,sli;
int lowbit(int x){return x&(-x);}
void update(int x,int k,int id){
    for(int i=x;i<N;i+=lowbit(i)) tr[id][i]+=k;
}
LL query(int x,int id){
    LL res=0;
    for(int i=x;i;i-=lowbit(i)) res+=tr[id][i];
    return res;
}
int main(){
    freopen("cake.in","r",stdin);
    freopen("cake.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d",t+i,l+i),st+=t[i],sl+=l[i],ml[i]=l[i],update(l[i],l[i],1),update(l[i],1,0);
    sort(ml+1,ml+n+1);for(int i=1;i<=n;i++) sli+=1ll*ml[i]*i;
    printf("%lld\n",st-sl*(n+1)+sli);
    while (m--){
        int k,ti,li;
        scanf("%d%d%d",&k,&ti,&li);
        st+=ti-t[k];
        sl+=li-l[k];
        sli-=query(l[k],0)*l[k];
        update(l[k],-1,0);update(l[k],-l[k],1);
        sli+=query(l[k],1)-query(li,1);
        update(li,1,0);update(li,li,1);
        sli+=query(li,0)*li;
        t[k]=ti;
        l[k]=li;
        printf("%lld\n",st-sl*(n+1)+sli);
    }
}

路在何方,路在脚下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值