【codeforces】660F. Bear and Bowling 4【分治+维护凸壳】

题目链接:【codeforces】660F. Bear and Bowling 4

题目大意:给一个长度为 n 的序列,第i个数为 ai 选取其中一个连续子序列 {ax,ax+1,...,ax+k1} ,使得 ki=1iax+i1 最大。
题目分析:考虑对序列分治,每次选取当前整个序列的中点,然后以左半部分每个下标为x,右边则可以表示成 kx+b 的形式,因此可以对右边的直线维护一个下凸壳。
复杂度 O(nlog2n) ,如果用归并排序代替快排,可以做到 O(nlogn)

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 200005 ;

struct Line {
    LL k , b ;
    Line () {}
    Line ( LL k , LL b ) : k ( k ) , b ( b ) {}
    bool operator < ( const Line& a ) const {
        return k != a.k ? k < a.k : b < a.b ;
    }
    LL f ( int x ) {
        return k * x + b ;
    }
} ;

Line a[MAXN] ;
LL sum[MAXN] ;
LL val[MAXN] ;
int S[MAXN] ;
LL ans ;
int n ;

int check ( int i , int j , int k ) {
    return ( double ) ( a[i].b - a[j].b ) / ( a[j].k - a[i].k ) >= ( double ) ( a[i].b - a[k].b ) / ( a[k].k - a[i].k ) ;
}

void dfs ( int l , int r ) {
    if ( l == r ) return ;
    int m = l + r >> 1 ;
    dfs ( l , m ) ;
    dfs ( m + 1 , r ) ;
    int cnt = 0 , t = 0 ;
    LL tot = 0 ;
    for ( int i = m + 1 ; i <= r ; ++ i ) {
        tot += ( i - m ) * val[i] ;
        a[++ cnt] = Line ( sum[i] - sum[m] , tot ) ;
    }
    sort ( a + 1 , a + cnt + 1 ) ;
    for ( int i = 1 ; i <= cnt ; ++ i ) {
        while ( t && a[S[t]].k == a[i].k || t > 1 && check ( S[t - 1] , S[t] , i ) ) -- t ;
        S[++ t] = i ;
    }
    tot = 0 ;
    for ( int i = m , j = 1 , k = 1 ; i >= l ; -- i , ++ j ) {
        tot += sum[m] - sum[i - 1] ;
        while ( k < t && a[S[k]].f ( j ) < a[S[k + 1]].f ( j ) ) ++ k ;
        ans = max ( ans , tot + a[S[k]].f ( j ) ) ;
    }
}

void solve () {
    ans = 0 ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%lld" , &val[i] ) ;
        sum[i] = sum[i - 1] + val[i] ;
        ans = max ( ans , val[i] ) ;
    }
    dfs ( 1 , n ) ;
    printf ( "%lld\n" , ans ) ;
}

int main () {
    while ( ~scanf ( "%d" , &n ) ) solve () ;
    return 0 ;
}

这是归并排序的代码,然而没有什么效率的提升。

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 200005 ;

struct Line {
    LL k , b ;
    Line () {}
    Line ( LL k , LL b ) : k ( k ) , b ( b ) {}
    bool operator < ( const Line& a ) const {
        return k != a.k ? k < a.k : b < a.b ;
    }
    LL f ( int x ) {
        return k * x + b ;
    }
} ;

Line a[MAXN] ;
LL sum[MAXN] ;
LL val[MAXN] ;
LL tot[MAXN] ;
int id[MAXN] ;
int S[MAXN] ;
LL ans ;
int n ;

int check ( int i , int j , int k ) {
    return ( double ) ( a[i].b - a[j].b ) / ( a[j].k - a[i].k ) >= ( double ) ( a[i].b - a[k].b ) / ( a[k].k - a[i].k ) ;
}

void merge ( int a[] , int n , int b[] , int m ) {
    static int c[MAXN] ;
    int i = 0 , j = 0 , k = 0 , t = 0 ;
    while ( i < n && j < m ) c[k ++] = sum[a[i]] < sum[b[j]] ? a[i ++] : b[j ++] ;
    while ( i < n ) c[k ++] = a[i ++] ;
    while ( j < m ) c[k ++] = b[j ++] ;
    while ( t < n + m ) a[t] = c[t] , t ++ ;
}

void dfs ( int l , int r ) {
    if ( l == r ) return ;
    int m = l + r >> 1 ;
    dfs ( l , m ) ;
    dfs ( m + 1 , r ) ;
    int cnt = 0 , t = 0 ;
    tot[m] = 0 ;
    for ( int i = m + 1 ; i <= r ; ++ i ) {
        tot[i] = tot[i - 1] + ( i - m ) * val[i] ;
    }
    for ( int i = m + 1 ; i <= r ; ++ i ) {
        a[++ cnt] = Line ( sum[id[i]] - sum[m] , tot[id[i]] ) ;
    }
    for ( int i = 1 ; i <= cnt ; ++ i ) {
        while ( t && a[S[t]].k == a[i].k && a[S[t]].b < a[i].b ) -- t ;
        while ( t > 1 && check ( S[t - 1] , S[t] , i ) ) -- t ;
        S[++ t] = i ;
    }
    LL res = 0 ;
    for ( int i = m , j = 1 , k = 1 ; i >= l ; -- i , ++ j ) {
        res += sum[m] - sum[i - 1] ;
        while ( k < t && a[S[k]].f ( j ) < a[S[k + 1]].f ( j ) ) ++ k ;
        ans = max ( ans , res + a[S[k]].f ( j ) ) ;
    }
    merge ( id + l , m - l + 1 , id + m + 1 , r - m ) ;
}

void solve () {
    ans = 0 ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%lld" , &val[i] ) ;
        sum[i] = sum[i - 1] + val[i] ;
        ans = max ( ans , val[i] ) ;
        id[i] = i ;
    }
    dfs ( 1 , n ) ;
    printf ( "%lld\n" , ans ) ;
}

int main () {
    while ( ~scanf ( "%d" , &n ) ) solve () ;
    return 0 ;
}

压缩后代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define clr(a,x) memset(a,x,sizeof a)
const int MAXN=200005;
struct Line{
    LL k,b;
    Line(){}
    Line(LL k,LL b):k(k),b(b){}
    bool operator <(const Line&a)const{return k!=a.k?k<a.k:b<a.b;}
    LL f(int x){return k*x+b;}
}a[MAXN];
LL sum[MAXN],val[MAXN],tot[MAXN],ans;
int id[MAXN],S[MAXN],n;
int check(int i,int j,int k){
    return(double)(a[i].b-a[j].b)/(a[j].k-a[i].k)>=(double)(a[i].b-a[k].b)/(a[k].k-a[i].k);
}
void merge(int a[],int n,int b[],int m){
    static int c[MAXN];
    int i=0,j=0,k=0,t=0;
    while(i<n&&j<m)c[k++]=sum[a[i]]<sum[b[j]]?a[i++]:b[j++];
    while(i<n)c[k++]=a[i++];
    while(j<m)c[k++]=b[j++];
    while(t<n+m)a[t]=c[t],t++;
}
void dfs(int l,int r){
    if(l==r)return;
    int m=l+r>>1;
    dfs(l,m);
    dfs(m+1,r);
    int cnt=0,t=0;
    tot[m]=0;
    for(int i=m+1;i<=r;++i)tot[i]=tot[i-1]+(i-m)*val[i];
    for(int i=m+1;i<=r;++i)a[++cnt]=Line(sum[id[i]]-sum[m],tot[id[i]]);
    for(int i=1;i<=cnt;S[++t]=i++){
        while(t&&a[S[t]].k==a[i].k&&a[S[t]].b<a[i].b)--t;
        while(t>1&&check(S[t-1],S[t],i))--t;
    }
    LL res=0;
    for(int i=m,j=1,k=1;i>=l;--i,++j){
        res+=sum[m]-sum[i-1];
        while(k<t&&a[S[k]].f(j)<a[S[k+1]].f(j))++k;
        ans=max(ans,res+a[S[k]].f(j));
    }
    merge(id+l,m-l+1,id+m+1,r-m);
}
void solve(){
    ans=0;
    for(int i=1;i<=n;++i){
        scanf("%lld",&val[i]);
        sum[i]=sum[i-1]+val[i];
        ans=max(ans,val[i]);
        id[i]=i;
    }
    dfs(1,n);
    printf("%lld\n",ans);
}
int main(){
    while(~scanf("%d",&n))solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值