也许是虚标,但更希望是我变强了,做完之后才发现是紫题。
感觉并没有想象中的复杂和难解。
题目描述
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 安排一天的比萨烘焙顺序,使得他一天赚取的总小费最大。
注意:
-
一天从时间单位 0 0 0 开始,你可以认为这一天是无限长的。
-
居民们有时会改变他们的 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(Tpi−∑j=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=1nTpi−∑i=1n∑j=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=1nTpi−∑i=1n(n−i+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=1ni⋅Lpi
显然前两项与排列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);
}
}
路在何方,路在脚下。