P2300 合并神犇(单调队列优化dp)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2300 合并神犇(单调队列优化dp)相关的知识,希望对你有一定的参考价值。
P2300 合并神犇(单调队列优化dp)
令 f i f_i fi表示前 i i i个数的最少合并次数, g i g_i gi表示在 f i f_i fi条件下的最小结尾值。
有转移方程: f i = m i n ( f j + i − j − 1 ) , s ( j , i ) ≥ g j f_i=min(f_j+i-j-1),s(j,i)\\ge g_j fi=min(fj+i−j−1),s(j,i)≥gj
因为: f j + 1 ≤ f j + 1 f_{j+1}\\le f_j+1 fj+1≤fj+1 即 j + 1 j+1 j+1, f j + 1 f_{j+1} fj+1至多会加1, 而 j j j加 1 , i + j − 1 1,i+j-1 1,i+j−1 必定会减 1 1 1
因此在满足 s ( j , i ) ≥ g j s(j,i)\\ge g_j s(j,i)≥gj 的情况下,肯定 j j j越大越好。
s i − s j ≥ g j → s i ≥ s j + g j s_i-s_j\\ge g_j \\rightarrow s_i\\ge s_j+g_j si−sj≥gj→si≥sj+gj。
因此考虑单调队列维护 s j + g j s_j+g_j sj+gj ,然后每次选择满足 s j + g j ≤ s i s_j+g_j \\le s_i sj+gj≤si 的条件下靠近队尾的,因为靠近队尾的 j j j更大。
然后维护一个单调递增的单调队即可,注意 n o d e node node 除了记录 s j + g j s_j+g_j sj+gj ,还要记录 j j j这个 i d id id 便于转移。
另外需要注意的是队列最开始要加入 ( 0 , 0 ) (0,0) (0,0)这个结点,因为有可能直接前面所有的数为为一组进行转移。
时间复杂度: O ( n ) O(n) O(n)
// Problem: P2300 合并神犇
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2300
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-08-10 10:02:52
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=2e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define ios ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\\n",a[n]);
}
ll f[N],g[N];
ll a[N];
int n;
int l=1,r=1;//这里相当于加入了(0,0) 因为有可能直接从(0,0) 转移
struct node{
int id;
ll w;
}q[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]),a[i]+=a[i-1];
}
for(int i=1;i<=n;i++){
while(l+1<=r&&q[l+1].w<=a[i]) l++;
f[i]=f[q[l].id]+i-q[l].id-1,g[i]=a[i]-a[q[l].id];
while(l<=r&&a[i]+g[i]<=q[r].w) r--;
q[++r]={i,a[i]+g[i]};
}
printf("%lld\\n",f[n]);
return 0;
}
以上是关于P2300 合并神犇(单调队列优化dp)的主要内容,如果未能解决你的问题,请参考以下文章