【CQBZOJ 3345】[树状数组]pizza

题目描述

线段树水题专项赛:Portal
Mirko的披萨店在镇上很受欢迎,每个人都把披萨作为午餐。Mirko提供外送服务,他的送货速度非常快,所以送货的时间可以忽略不计。镇上每个人都有自己最喜欢的口味,所以,Mirko给每个人做的披萨需要不同的时间。他只有一个小烤炉,每次只能烤一个披萨。如果他给某个人的披萨早于那个人的午餐时间k个时间单位,那么他可以收到k单位的小费,反之,如果晚于客户的午餐时间k个时间单位,那么他将损失k单位的钱。所以,Mirko需要提前安排好每个披萨制作的次序,以保证他的小费尽量多。每个客户的午餐时间是确定的,他的披萨需要花多少时间做好也是确定的。但是,可能客户会有所改变,一旦改变,那么mirko可能需要调整他的计划。Mirko从时刻0开始制作披萨,对于每一次改变,请输出现在小费能达到的最大值。
人数 n200000 ,任何时间 100000

题目解析

我们可以先贪心一下,因为答案为 ,即我们要让制作时间最小,又因为第 i 个制作的披萨代价是ti×(ni+1),很容易就可以发现时间越短的披萨越先做越好。
初始的小费是可以直接排序算出来的,那怎么算修改后的呢?
对于每一次修改,我们可以看做删除一个订单再加入一个订单,删除的收益为 w×(nrankw+1)+sumrankw rankw w 在原定单中的排名,sumrankw是在 w 排名之前的订单时间之和,至于为什么请自行人脑模拟。那么rankw sumrankw 就可以直接用两个树状数组维护了。
(mdzz,我一来咋想Splay去了呢,一定是我最近Splay写多了,跑得shi慢)

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#include<vector>
#include<algorithm>
#include<cstdlib>
#include<queue>
using namespace std;

#define MAXN 200000
#define MAXM 100000
#define INF 0x3f3f3f3f
typedef long long int LL;

template<class T>
void Read(T &x){
    x=0;char c=getchar();bool flag=0;
    while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    if(flag)x=-x;
}

int n,m;
int lim[MAXN+10],t[MAXN+10];
int tmp[MAXN+10];

int sz[MAXM+10];
LL sum[MAXM+10];

int lowbit(int &x){return x&-x;}
void add(int x,int k,int val){
    while(x<=MAXM){
        sz[x]+=k;
        sum[x]+=1ll*k*val;
        x+=lowbit(x);
    }
}

int queryrank(int x){
    int rn=0;
    while(x>0){
        rn+=sz[x];
        x-=lowbit(x);
    }
    return rn;
}

LL querysum(int x){
    LL rn=0;
    while(x>0){
        rn+=sum[x];
        x-=lowbit(x);
    }
    return rn;
}

int main(){
    //freopen("pizza.in","r",stdin);
    //freopen("pizza.out","w",stdout);

    Read(n),Read(m);

    for(int i=1;i<=n;++i){
        Read(lim[i]);
        Read(t[i]);
        tmp[i]=t[i];
    }

    sort(tmp+1,tmp+n+1);

    LL ans=0;
    for(int i=1;i<=n;++i){
        ans+=lim[i];
        ans-=1ll*tmp[i]*(n-i+1);
    }

    for(int i=1;i<=n;++i)
        add(t[i],1,t[i]);

    printf("%lld\n",ans);

    int id,new_lim,new_t;
    for(int i=1;i<=m;++i){
        Read(id),Read(new_lim),Read(new_t);
        ans-=lim[id];
        ans+=(lim[id]=new_lim);

        ans+=1ll*t[id]*(n-(queryrank(t[id]-1)+1)+1);
        ans+=querysum(t[id]-1);

        add(t[id],-1,t[id]);
        t[id]=new_t;
        add(t[id],1,t[id]);

        ans-=1ll*t[id]*(n-(queryrank(t[id]-1)+1)+1);
        ans-=querysum(t[id]-1);

        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值