题解 门童
Description
有 \(n\) 个人需要接待,每个人可以被接待的时间为 \([L_i,R_i]\),你提前 \(j\) 分钟接待他的话可以产生 \(j\times p_i\) 的贡献。可以在门口和沙发之间来回,需要时间为 \(L\),待在门口或者辗转都会减少贡献,在沙发上会增加贡献。
问接待完所有人时贡献最大值。\(n\le 10^5,L_i,R_i,p_i\le 10^9\)
Solution
考场上面sb了。
可以想到的是,会辗转的时间一定在 \(L_i,R_i\) ,所以我们可以离散化,然后设 \(f_i\) 表示第 \(i\) 个时间在门口的最大贡献。
我们将需要服务的人按 \(L_i\) 排序,那么一次一定服务一段合法区间最优。然后你们 \(f_i\) 就是由 \(f_{i-1}\) 已经一段区间的 \(f_j\) 转移而来。dp 式非常好列就不写了。
然后你发现可以斜优,就做完了。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m,L,x[2][2],f[MAXN],Ed[MAXN],tmp[MAXN],mpos[MAXN],mxpos[MAXN],pre1[MAXN],pre2[MAXN],Pre1[MAXN],Pre2[MAXN],minn[MAXN][21];
struct node{
int st,ed,P;
bool operator < (const node &p)const{return st < p.st;}
}s[MAXN];
void init (){
for (Int i = 1;i <= n;++ i) minn[i][0] = s[i].ed;
for (Int j = 1;(1 << j) <= n;++ j)
for (Int i = 1;i + (1 << j) - 1 <= n;++ i)
minn[i][j] = min (minn[i][j - 1],minn[i + (1 << j - 1)][j - 1]);
}
int query (int l,int r){
if (l > r) return 1e18;
int k = log2 (r - l + 1);
return min (minn[l][k],minn[r - (1 << k) + 1][k]);
}
int head,tail,sta[MAXN];
int getb (int j){return f[j] - tmp[j] * x[1][1] - pre1[j];}
int geta (int j){return pre2[j];}
int son (int j1,int j2){return getb (j2) - getb (j1);}
int mot (int j1,int j2){return geta (j2) - geta (j1);}
signed main(){
// freopen ("large_2.in","r",stdin);
// freopen ("f1.out","w",stdout);
read (n,L);
for (Int i = 0;i < 2;++ i) for (Int j = 0;j < 2;++ j){
read (x[i][j]);
if (!i || !j) x[i][j] = -x[i][j];
}
for (Int i = 1;i <= n;++ i){
int st,ed,p;read (st,ed,p);
ed += st,s[i] = node{st,ed,p};
}
sort (s + 1,s + n + 1);
for (Int i = 1;i <= n;++ i) Pre1[i] = Pre1[i - 1] + s[i].ed * s[i].P,Pre2[i] = Pre2[i - 1] + s[i].P;
for (Int i = 1;i <= n;++ i) tmp[++ m] = s[i].st,tmp[++ m] = s[i].ed;
sort (tmp + 1,tmp + m + 1),m = unique (tmp + 1,tmp + m + 1) - tmp - 1;
memset (f,0xcf,sizeof (f)),f[0] = 0,init ();
for (Int i = 1;i <= m;++ i){
int tim = tmp[i];
int l = 1,r = n,ans = 0;
while (l <= r){
int mid = l + r >> 1;
if (s[mid].st <= tim) ans = mid,l = mid + 1;
else r = mid - 1;
}
Ed[i] = ans,pre1[i] = Pre1[ans],pre2[i] = Pre2[ans],l = 0,r = i - 1,ans = i;
while (l <= r){
int mid = l + r >> 1;
if (tim <= query (Ed[mid] + 1,Ed[i])) ans = mid,r = mid - 1;
else l = mid + 1;
}
mpos[i] = ans,l = 0,r = i - 1,ans = 0;
while (l <= r){
int mid = l + r >> 1;
if (tmp[i] - tmp[mid] >= 2 * L && Ed[mid] < n) ans = mid,l = mid + 1;
else r = mid - 1;
}
mxpos[i] = ans;
}
int now = 0;sta[head = tail = 1] = 0;
for (Int i = 1;i <= m;++ i){
while (now < mxpos[i]){
now ++;
while (tail - head + 1 >= 2 && son (sta[tail - 1],sta[tail]) * mot (sta[tail],now) <= son (sta[tail],now) * mot (sta[tail - 1],sta[tail])) -- tail;
sta[++ tail] = now;
}
while (head <= tail && sta[head] < mpos[i]) ++ head;
if (mpos[i] <= mxpos[i]){
while (head < tail && son (sta[head],sta[head + 1]) > -tmp[i] * mot (sta[head],sta[head + 1])) ++ head;
if (head <= tail){
int j = sta[head];
f[i] = getb (j) + geta (j) * tmp[i] + L * (x[0][1] + x[1][0]) + (tmp[i] - 2 * L) * x[1][1] + pre1[i] - tmp[i] * pre2[i];
}
}
chkmax (f[i],f[i - 1] + (tmp[i] - tmp[i - 1]) * x[0][0] + pre1[i] - pre1[i - 1] - tmp[i] * (pre2[i] - pre2[i - 1]));
}
int res = -1e18;
for (Int i = 1;i <= m;++ i) if (Ed[i] == n) chkmax (res,f[i]);
write (res),putchar ('\n');
return 0;
}