当前位置: 代码迷 >> 综合 >> 【Educational Codeforces Round 11-F. Bear and Bowling 4】斜率优化DP+二分
  详细解决方案

【Educational Codeforces Round 11-F. Bear and Bowling 4】斜率优化DP+二分

热度:18   发布时间:2023-12-29 01:56:11.0

Bear and Bowling 4

题目链接:

https://codeforces.com/contest/660/problem/F

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

6
5 -1000 1 -3 7 -8

Sample Output

16

Hint

在这里插入图片描述

题意

定义一个长度为n的数组a的权值为∑i=1ni?a[i]\sum_{i=1}^{n} i*a[i]i=1n?i?a[i],给出一个数组,找出一个连续子序列,让这个子序列的权值最大。

题解

首先想一下n^2的DP方程。

我们设dp[i]=∑j=1ij?a[j],sum[i]=∑j=1ia[j]dp[i]=\sum_{j=1}^{i} j*a[j],sum[i]=\sum_{j=1}^{i} a[j]dp[i]=j=1i?j?a[j]sum[i]=j=1i?a[j]

那么以i为结尾的最大权值的连续子序列的权值为ans[i]

ans[i]=max{dp[i]?dp[j]?j?(sum[i]?sum[j])}...(0≤j<i)ans[i] = max\{dp[i]-dp[j]-j*(sum[i]-sum[j])\} ...(0\leq j < i)ans[i]=max{ dp[i]?dp[j]?j?(sum[i]?sum[j])}...(0j<i)

看这个方程大概就可以斜率DP。

我们进行推导一下,设k<j<ik<j<ik<j<i ,当jjjkkk更优的时候:

? dp[i]?dp[j]?j?(sum[i]?sum[j])>dp[i]?dp[k]?k?(sum[i]?sum[k])dp[i]-dp[j]-j*(sum[i]-sum[j]) > dp[i]-dp[k]-k*(sum[i]-sum[k])dp[i]?dp[j]?j?(sum[i]?sum[j])>dp[i]?dp[k]?k?(sum[i]?sum[k])

=>dp[j]+j?sum[i]?j?sum[j]<dp[k]+k?sum[i]?k?sum[k]=> dp[j]+j*sum[i]-j*sum[j]<dp[k]+k*sum[i]-k*sum[k]=>dp[j]+j?sum[i]?j?sum[j]<dp[k]+k?sum[i]?k?sum[k]

=>sum[i]?(j?k)<dp[k]?k?sum[k]?(dp[j]?j?sum[j])=> sum[i]*(j-k) < dp[k]-k*sum[k]-(dp[j]-j*sum[j])=>sum[i]?(j?k)<dp[k]?k?sum[k]?(dp[j]?j?sum[j])

=>sum[i]?(j?k)<j?sum[j]?dp[j]?(k?sum[k]?dp[k])=> sum[i]*(j-k)<j*sum[j]-dp[j]-(k*sum[k]-dp[k])=>sum[i]?(j?k)<j?sum[j]?dp[j]?(k?sum[k]?dp[k])

=>sum[i]<j?sum[j]?dp[j]?(k?sum[k]?dp[k])(j?k)=>sum[i]< \frac{j*sum[j]-dp[j]-(k*sum[k]-dp[k])}{(j-k)}=>sum[i]<(j?k)j?sum[j]?dp[j]?(k?sum[k]?dp[k])?

我们发现如果设f[x]=x?sum[x]?dp[x]f[x]=x*sum[x]-dp[x]f[x]=x?sum[x]?dp[x] 那么sum[i]sum[i]sum[i]就是g(j,k)g(j,k)g(j,k)的斜率。

还是设k<j<ik<j<ik<j<i ,如果存在g[i,j]>g[j,k]g[i,j]>g[j,k]g[i,j]>g[j,k],那么j这个点就一定是没用的,这里我们来证明一下。

1.当g[i,j]>sum[i]g[i,j]>sum[i]g[i,j]>sum[i],说明iiijjj更优,此时j是没用的。

2.当g[i,j]≤sum[i]g[i,j]\leq sum[i]g[i,j]sum[i],但是又存在g[j,k]<g[i,j]≤sum[i]g[j,k]<g[i,j] \leq sum[i]g[j,k]<g[i,j]sum[i],由于g[j,k]<sum[i]g[j,k]<sum[i]g[j,k]<sum[i]说明此时k是比j更优的,此时j也是没用的。

所以我们可以通过上面的优化去除掉所有g[i,j]>g[j,k]g[i,j]>g[j,k]g[i,j]>g[j,k]的情况,所以从左到右斜率呈现一个递减的情况,也就是一个上凸包。

但是由于本题中sum[i]不是递增的,所以不能通过弹出队首的方法优化斜率DP,我们只能在维护好的凸包上二分找到最优解。

二分的时候设F(mid)F(mid)F(mid)为mid能得到的最大值,如果F(mid)>=F(mid+1)F(mid)>=F(mid+1)F(mid)>=F(mid+1)说明最优解在左侧,否则在右侧。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<time.h>
#include<math.h>
using namespace std;//***********************IO**********************************
namespace fastIO
{
    #define BUF_SIZE 100000#define OUT_SIZE 100000bool IOerror=0;inline char nc(){
    static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;if (p1==pend){
    p1=buf;pend=buf+fread(buf,1,BUF_SIZE,stdin);if (pend==p1){
    IOerror=1;return -1;}}return *p1++;}inline bool blank(char ch){
    return ch==' '|ch=='\n'||ch=='\r'||ch=='\t';}inline void read(int &x){
    bool sign=0;char ch=nc();x=0;for (; blank(ch); ch=nc());if (IOerror)return;if (ch=='-')sign=1,ch=nc();for (; ch>='0'&&ch<='9'; ch=nc())x=x*10+ch-'0';if (sign)x=-x;}#undef OUT_SIZE#undef BUF_SIZE
};
using namespace fastIO;
//************************************************************************#define ok cout<<"OK"<<endl;
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl;
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl;
#define print(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define pb push_back
#define Fi first
#define Se second
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pil pair<int,ll>
#define pll pair<ll,ll>const double eps = 1e-8;
const double PI = acos(-1.0);
const int Mod = 1000000007;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 2e5+10;
ll dp[maxn],sum[maxn],a[maxn],res[maxn];
int q[maxn],top;
ll GET(int i,int j)
{
    return dp[i]-dp[j]-1LL*j*(sum[i]-sum[j]);
}
ll Y(int x)
{
    return 1LL*x*sum[x]-dp[x];
}
int main()
{
    int n;scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%lld",&a[i]);for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i],dp[i]=dp[i-1]+1LL*i*a[i];q[++top]=0;for(int i=1;i<=n;i++){
    int l=1,r=top-1;while(l<=r){
    int mid=(l+r)>>1;if(GET(i,q[mid])>=GET(i,q[mid+1])) r=mid-1;else l=mid+1;}res[i]=max(a[i],GET(i,q[l]));while(top&&(Y(i)-Y(q[top]))*(q[top]-q[top-1])>(Y(q[top])-Y(q[top-1]))*(i-q[top])) top--;q[++top]=i;}ll ans=0;for(int i=1;i<=n;i++) ans=max(ans,res[i]);printf("%lld\n",ans);return 0;
}
  相关解决方案