Codeforces Round #842 (Div. 2) F. Wonderful Jump

传送门


挺有意思一道题
首先 n 2 n^2 n2 d p dp dp 非常显然 考虑优化
复杂度不难想到大概是个分块
考虑一个显然的结论就是 如果 m i n { a i . . . . . a j } ∗ ( i − j ) 2 > n ∗ ( i − j ) min\{a_i.....a_j\}*(i-j)^2>n*(i-j) min{ai.....aj}(ij)2>n(ij) 的话 转移不如一个个跳 因此就不转移了

① 若 m i n { a i . . . a j } > n min\{a_i...a_j\}>\sqrt{n} min{ai...aj}>n , 这种情况只要转移附近的 n \sqrt{n} n 即可
注意意思不是前面不转移了,因为对于更前面的 j j j 是有可能不满足条件的 这个属于后面的情况

② < n \sqrt{n} n
有一个结论就是 如果 a i . . . . . a j a_i.....a_j ai.....aj 中最小值不在两端 ,那么显然从中间跳更优
那么只要维护一个小于 n \sqrt{n} n 的单调栈,只在栈内转移即可

可以简单讨论一下 a i a_i ai 为最小或者 a j a_j aj 最小的情况
做法就很显然了
如果 a j < n a_j < \sqrt{n} aj<n 往前扫,扫到比自己大的就不转移了,然后同时从单调栈内所有的元素再转移一次

一开始我以为是排列
这里因为元素有重复 ,所以一定要加扫到比自己大的就不转移,以及单调栈是严格的= =
一开始写潦草了结果 TLE 了

#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define ll long long
#define fr first
#define se second
#define mp make_pair
#define pb push_back
#define P pair<int,int>
int rd() {
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();  
    if(flag) return sum;
    else return -sum;
}
int n;
int a[401000];
ll f[401000];
int st[401000];
int main() {
    n = rd();
    int K = (int)sqrt(n) + 1;
    rep(i,1,n) a[i] = rd();
    rep(i,2,n) f[i] = (long long) 2e18;
    rep(i,2,n) {
        int mn = a[i];
        repp(j,i-1,max(1,i-K)) {
            mn = min( mn , a[j] );
            f[i] = min( f[i] , f[j] + 1ll*mn*(i-j)*(i-j) );
        }
        mn = a[i];
        if( a[i] <= K )
            repp(j,i-1,1){
                mn = min( mn , a[j] );
                f[i] = min( f[i] , f[j] + 1ll*mn*(i-j)*(i-j) );
                if(a[j] <= a[i]) break;
            }
        repp(j,st[0],1) {
            int id = st[j];
            f[i] = min( f[i] , f[id] + 1ll*a[id]*(i-id)*(i-id) );
        }
        if( a[i] <= K ) {
            while( a[i] < a[st[st[0]]] && st[0] > 0 ) st[0]--;
            st[++st[0]] = i;
        }
    }
    rep(i,1,n) printf("%lld ",f[i]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值