Gym - 101806R :Recipe(分治+斜率优化)
题意:有一个厨师,他买菜-做菜-买菜-做菜....-做菜,一共有N天,他的冰箱里只能有一个菜,在他做菜的第二天才会买菜,如果菜不做,放在冰箱里,每天新鲜程度会下降1。 第一天也会买菜,第i天的菜新鲜程度的Fi,厨艺是Ci,(厨艺会增长),要求这一天做的菜的新鲜长度大于等于Li。
思路:即dpi=max dp[j]+[F[j+1]-(i-(j+1))]*Ci (满足Li>=F[j+1]-(i-j-1)); 表示成b=y+kx的可以求最大值,表示成b=y-kx的可以求最小值。
由于k=Ci是单增的,所以想到斜率优化,可以把原方程化为:dp i=max dpj +[F[j+1]+(j+1)]*Ci - i*Ci 满足(Li+i>=F[j+1]+j+1);
而由于有括号里的限制,我们不能简单的斜率优化DP。 而考虑分治,分治的情况下,我们可以把Mid左边的按照Fi+i排序,右边的按照Li+i排序,如果满足括号条件,则把左边加入凸包, 右边更新答案。 这个时候由于不是按下标插入,所以x不是单调的,所以我们要在凸包上二分答案。
#include<bits/stdc++.h> #define pii pair<ll,int> #define mp make_pair #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=300010; const int inf=1e9; ll dp[maxn],F[maxn],C[maxn],L[maxn]; pii a[maxn],b[maxn]; int q[maxn],top; ll getans(int p,int k) { return dp[k]+C[p]*F[k+1]-C[p]*p; } void update(int p) { if(top==0) return;int L=1,R=top-1,Mid; dp[p]=max(dp[p],getans(p,q[top])); while(L<=R){ Mid=(L+R)>>1; ll tmp1=getans(p,q[Mid]),tmp2=getans(p,q[Mid+1]); if(tmp1>tmp2) R=Mid-1,dp[p]=max(dp[p],tmp1); else L=Mid+1,dp[p]=max(dp[p],tmp2); } } bool check(int p){ return (dp[p]-dp[q[top]])*(F[p+1]-F[q[top-1]+1])<= (dp[p]-dp[q[top-1]])*(F[p+1]-F[q[top]+1]); } void add(int p) { if(dp[p]==-inf) return ; if(top>0&&F[p+1]==F[q[top]+1]&&dp[p]>dp[q[top]]) top--; while(top>1&&check(p)) top--; q[++top]=p; } void solve(int Le,int Ri) { if(Le==Ri) return ; int Mid=(Le+Ri)>>1; solve(Le,Mid); int tot1=0,tot2=0; top=0; rep(i,Le,Mid) a[++tot1]=mp(F[i+1],i); rep(i,Mid+1,Ri) b[++tot2]=mp(L[i],i); sort(a+1,a+tot1+1); sort(b+1,b+tot2+1); reverse(a+1,a+tot1+1); reverse(b+1,b+tot2+1); for(int i=1,j=1;i<=tot2;i++){ while(j<=tot1&&b[i].first<=a[j].first){ add(a[j].second); j++; } update(b[i].second); } solve(Mid+1,Ri); } int main() { int N; scanf("%d",&N); rep(i,1,N) scanf("%lld",&F[i]),F[i]+=i; rep(i,1,N) scanf("%lld",&C[i]); rep(i,1,N) scanf("%lld",&L[i]),L[i]+=i; rep(i,1,N) dp[i]=-inf; solve(0,N); if(dp[N]==-inf) puts("Impossible"); else printf("%lld\n",dp[N]); return 0; }
It is your time to fight!