Codeforces Gym 101175F - Machine Works(CDQ 分治维护斜率优化)

题面传送门

首先很明显我们会按照 \(d_i\) 的顺序从小到大买这些机器,故不管三七二十一先将所有机器按 \(d_i\) 从小到大排序。

考虑 \(dp\),\(dp_i\) 表示在时刻 \(d_i\) 及以前卖掉手头的机器,最多能剩下多少钱。

转移显然就枚举上一个购买的机器编号 \(j\),即 \(dp_i=\max\limits_{j=1}^{i-1}dp_j-p_j+g_j(d_i-d_j-1)+r_j\),其中 \(j\) 可以转移到 \(i\) 当前仅当 \(dp_j\geq p_j\)。最终答案可以选择在某一时刻卖完某个机器后就不再买入新的机器了,即 \(\max\limits_{i=1}^ndp_i\),也可以选择将某个机器用到 \(D\) 天结束,即 \(\max\limits_{i=1}^ndp_i-p_i+g_i(D-d_i)+r_i(dp_i\ge p_i)\)

这样暴力转移是 \(n^2\) 的,但是有一个很明显的优化是这个 \(dp\) 转移满足斜率优化的 \(f_i=d_i+\max a_ib_j+c_j\) 的形式,故考虑斜率优化。考虑两个决策点 \(j,k\) 满足 \(g_j>g_k\),那么从 \(j\) 转移比从 \(k\) 转移更优当前仅当:

\[\begin{aligned}&dp_j-p_j+g_j(d_i-d_j-1)+r_j>dp_k-p_k+g_k(d_i-d_k-1)+r_k\\\Leftrightarrow\ &dp_j-p_j-g_j(d_j+1)+r_j+g_jd_i>dp_k-p_k-g_k(d_k+1)+r_k+g_kd_i\\\Leftrightarrow\ &(dp_j-p_j-g_j(d_j+1)+r_j)-(dp_k-p_k-g_k(d_k+1)+r_k)>d_i(g_k-g_j)\\\Leftrightarrow\ &\dfrac{(dp_j-p_j-g_j(d_j+1)+r_j)-(dp_k-p_k-g_k(d_k+1)+r_k)}{g_j-g_k}>-d_i\end{aligned}
\]

斜率优化转移一下即可。

由于此题 \(g_i\) 不单调,故考虑 CDQ 分治维护斜率优化,具体来说,假设我们要用 \([l,mid]\) 中点的贡献更新 \(dp_{mid+1},dp_{mid+2},\dots,dp_r\),我们将 \([l,mid]\) 中所有满足要求的决策点按 \(g_i\) 从小到大排个序并依次加入凸壳,查找 \(d_i\) 的贡献时就在凸包上二分第一个斜率大于 \(-d_i\) 的点并转移即可。

还有就是对于 \(g_j=g_k\) 的 \(j,k\),相当于平面上坐标为 \(A(g_j,dp_j-p_j-g_j(d_j+1)+r_j),B(g_k,dp_k-p_k-g_k(d_k+1)+r_k)\) 的两点,那么我们对于横坐标相同的点肯定是先将纵坐标小的加入凸包,再将纵坐标大的加入凸包,因此在排序过程中要按\(g_j\) 为第一关键字,\(dp_j-p_j-g_j(d_j+1)+r_j\) 为第二关键字排序。我因此 WA 了好多好多发,xtbz。

这是萌新第二次写 CDQ 分治维护斜率优化哦,大佬不喜勿喷

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const ll INF=0x3f3f3f3f3f3f3f3fll;
int n,c,d,Casen=0;
struct event{
int d,p,r,g,id;
bool operator <(const event &rhs){
return d<rhs.d;
}
} a[MAXN+5],tmp[MAXN+5];
ll dp[MAXN+5];
int q[MAXN+5],tl=0;
double slope(int j,int k){
if(a[j].g==a[k].g) return INF;
return 1.0*((dp[j]-1ll*a[j].d*a[j].g-a[j].g+a[j].r-a[j].p)-(dp[k]-1ll*a[k].d*a[k].g-a[k].g+a[k].r-a[k].p))/(a[j].g-a[k].g);
}
int bsearch(int l,int r,int sl){
if(l==r) return l;int mid=l+r>>1;
if(slope(q[mid],q[mid+1])>sl) return bsearch(mid+1,r,sl);
else return bsearch(l,mid,sl);
}
void cdq(int l,int r){
if(l==r){chkmax(dp[l],dp[l-1]);return;}
int mid=l+r>>1;cdq(l,mid);for(int i=l;i<=mid;i++) tmp[i]=a[i];
sort(tmp+l,tmp+mid+1,[](event lhs,event rhs){
if(lhs.g^rhs.g) return lhs.g<rhs.g;int j=lhs.id,k=rhs.id;
return (dp[j]-1ll*a[j].d*a[j].g-a[j].g+a[j].r-a[j].p)<(dp[k]-1ll*a[k].d*a[k].g-a[k].g+a[k].r-a[k].p);
});
tl=0;
for(int i=l;i<=mid;i++){
if(dp[tmp[i].id]<tmp[i].p) continue;
while(tl&&slope(q[tl],tmp[i].id)>slope(q[tl-1],q[tl])) tl--;
q[++tl]=tmp[i].id;
}
for(int i=mid+1;i<=r;i++){
if(!tl) continue;
int pos=bsearch(1,tl,-a[i].d),id=q[pos];
chkmax(dp[i],dp[id]+1ll*(a[i].d-a[id].d-1)*a[id].g+a[id].r-a[id].p);
// printf("%d %d\n",i,id);
}
cdq(mid+1,r);
}
void solve(){
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&a[i].d,&a[i].p,&a[i].r,&a[i].g);
} sort(a+1,a+n+1);
for(int i=1;i<=n;i++) a[i].id=i;
memset(dp,0,sizeof(dp));
ll ans=c;dp[0]=c;cdq(1,n);
for(int i=1;i<=n;i++) if(dp[i]>=a[i].p)
chkmax(ans,dp[i]+a[i].r+1ll*(d-a[i].d)*a[i].g-a[i].p);
for(int i=1;i<=n;i++) chkmax(ans,dp[i]);
printf("Case %d: %lld\n",++Casen,ans);
}
int main(){
while(~scanf("%d%d%d",&n,&c,&d)){
if(!n||!c||!d) break;solve();
}
return 0;
}
上一篇:ios NSString 转 float的注意


下一篇:JBPM6教程