洛谷 P2300 合并神犇

洛谷

听说这题可以\(n^2\)水过去,不过这里介绍一种\(O(n)\)的做法。

\(f[i]\)为第\(1~i\)位合并的次数。

\(pre[i]\)为第\(1~i\)位最末尾的数。

\(j\)为满足\(sum[i]−sum[j]>=pre[j]\)的最大数。

所以很好推出:

\(f[i]=f[j]+i−j−1~~~~~pre[i]=sum[i]−sum[j]\)

显然\(pre[i]\)越小越好,这样找到一个就可以退出。

所以可以直接用单调队列优化。

时间复杂度\(O(n)\)

代码,注意开\(\texttt{long~long}\)

#include <bits/stdc++.h>
using namespace std;
typedef int _int;
#define int long long

const int N=200010;
int head,tail=1;
int n,f[N],pre[N],sum[N],q[N];

_int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    int x;
    for (int i=1;i<=n;++i)
        cin>>x,sum[i]=sum[i-1]+x;
    for (int i=1;i<=n;++i) {
        while (head+1<tail&&sum[i]>=sum[q[head+1]]+pre[q[head+1]])
            ++head;
        f[i]=f[q[head]]+1;
        pre[i]=sum[i]-sum[q[head]];
        while (head<tail&&sum[q[tail-1]]+pre[q[tail-1]]>sum[i]+pre[i])
            --tail;
        q[tail++]=i;
    }
    cout<<n-f[n];
    return 0;
}
posted @ 2018-09-07 09:48  fuyan0101  阅读(168)  评论(0编辑  收藏  举报