dp专题训练
Frog 1
我们设
当然这个方程在转移的时候不能越界。
于是做一个线性
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int n,h[N],f[N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>h[i];
}
memset(f,0x3f,sizeof f);
f[1]=0;
for(int i=1;i<=n;i++){
if(i>1){
f[i]=min(f[i],f[i-1]+abs(h[i]-h[i-1]));
}
if(i>2){
f[i]=min(f[i],f[i-2]+abs(h[i]-h[i-2]));
}
}
cout<<f[n];
return 0;
}
Frog 2
我们设
于是做一个线性
这里给个思考题:
怎么做?
显然是好做的,答案就是
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int n,k,h[N],f[N];
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>h[i];
}
memset(f,0x3f,sizeof f);
f[1]=0;
for(int i=1;i<=n;i++){
for(int j=max(1ll,i-k);j<i;j++){
f[i]=min(f[i],f[j]+abs(h[i]-h[j]));
}
}
cout<<f[n];
return 0;
}
Vacation
首先我们按照原来的想法设
设
转移方程非常好推:
提示
:设状态后发现这个状态出现了问题,可以把出现问题的这个东西加到状态里。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int n,a[N],b[N],c[N],f[N][3];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i];
}
f[1][0]=a[1];
f[1][1]=b[1];
f[1][2]=c[1];
for(int i=2;i<=n;i++){
f[i][0]=max(f[i-1][1],f[i-1][2])+a[i];
f[i][1]=max(f[i-1][0],f[i-1][2])+b[i];
f[i][2]=max(f[i-1][0],f[i-1][1])+c[i];
}
cout<<max({f[n][0],f[n][1],f[n][2]});
return 0;
}
Knapsack 1
我们考虑设
到这里已经可以通过了,但是我们还能做的更好。
可以发现
提示
:在空间比较紧张且 的转移只需要用到 时,可以采用滚动数组优化,把其中一维的空间变为常数级别。
事实上可以继续优化。我们可以把第一维直接优化掉,我们在循环
补充一句,完全背包也可以这样做,但是应用完全背包时
代码:
#include<bits/stdc++.h>
#define int long long
#define N 105
#define M 100005
using namespace std;
int n,m,w[N],v[N],f[M];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w[i]>>v[i];
}
for(int i=1;i<=n;i++){
for(int j=m;j>=w[i];j--){
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
cout<<f[m];
return 0;
}
Knapsack 2
我们考虑设
但是,可以发现
-
。 -
。
答案非常好求,就是找到最大的
提示
:如果一个状态会炸空间并且无法优化且此时答案的值域很小,可以将答案作为状态,状态作为答案。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 105
#define M 100005
using namespace std;
int n,m,w[N],v[N],f[N][M];
signed main(){
cin>>n>>m;
int sum=0;
for(int i=1;i<=n;i++){
cin>>w[i]>>v[i];
sum+=v[i];
}
memset(f,0x3f,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=sum;j>=0;j--){
f[i][j]=min(f[i][j],f[i-1][j]);
if(j>=v[i])f[i][j]=min(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}
int res=0;
for(int i=sum;i>=0;i--){
if(f[n][i]<=m){
res=i;
break;
}
}
cout<<res;
return 0;
}
LCS
最长公共子序列的状态十分经典。设
当
这样我们就可以求出整个序列的最长公共子序列的长度了。我们可以从这个倒推出方案。
分三种情况讨论:
-
与 相同且 :那么答案的第 位就是 。然后 。 -
与 相同: 。 -
与 相同: 。
这里事实上就是枚举
代码:
#include<bits/stdc++.h>
#define int long long
#define N 3005
using namespace std;
int n,m,f[N][N];
char s[N],t[N],res[N];
signed main(){
cin>>s+1>>t+1;
n=strlen(s+1);
m=strlen(t+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
f[i][j]=max({f[i-1][j],f[i][j-1]});
if(s[i]==t[j])f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
}
int i=n,j=m;
while(f[i][j]!=0){
if(s[i]==t[j]&&f[i][j]==f[i-1][j-1]+1){
res[f[i][j]]=s[i];
i--;j--;
}
else if(f[i][j]==f[i-1][j])i--;
else if(f[i][j]==f[i][j-1])j--;
}
for(int i=1;i<=f[n][m];i++){
cout<<res[i];
}
return 0;
}
Longest Path
我们设
写一个拓扑排序进行转移即可。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int n,m,h[N],e[N],ne[N],idx,din[N],f[N];
void add(int a,int b){
e[idx]=b;ne[idx]=h[a];h[a]=idx++;din[b]++;
}
signed main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
add(a,b);
}
queue<int>q;
for(int i=1;i<=n;i++){
if(din[i]==0){
q.push(i);
}
}
while(!q.empty()){
int t=q.front();
q.pop();
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
f[j]=max(f[j],f[t]+1);
if(--din[j]==0)q.push(j);
}
}
int res=0;
for(int i=1;i<=n;i++){
res=max(res,f[i]);
}
cout<<res;
return 0;
}
Grid 1
我们设 #
,则 #
,有转移方程:
这里给个思考题:
- 没有障碍且
怎么做。
答案是
代码:
#include<bits/stdc++.h>
#define int long long
#define N 1005
#define mod 1000000007
using namespace std;
int n,m,f[N][N];
char a[N][N];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
if(a[1][1]!='#')f[1][1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='#')continue;
if(i==1&&j==1)continue;
f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
}
}
cout<<f[n][m];
return 0;
}
Coins
我们设
于是最终答案为
代码:
#include<bits/stdc++.h>
#define int long long
#define N 3005
using namespace std;
int n;
double p[N],f[N][N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i];
}
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
f[i][j]+=(1-p[i])*f[i-1][j];
if(j!=0)f[i][j]+=p[i]*f[i-1][j-1];
}
}
double res=0;
for(int i=n/2+1;i<=n;i++){
res+=f[n][i];
}
cout<<setiosflags(ios::fixed)<<setprecision(10);
cout<<res;
return 0;
}
Sushi
首先设
首先会发现空间炸了,但是我们先不管空间,先去推一下转移方程:
考虑分讨吃到的盘子中的寿司数量,于是有:
移项得到:
然后我们有
发现现在的方程除了空间太大没有其他问题,又因为
最终的方程:
代码:
#include<bits/stdc++.h>
#define int long long
#define N 305
using namespace std;
int n,a[N];
double f[N][N][N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
a[x]++;
}
for(int k=0;k<=n;k++){
for(int j=0;j<=n;j++){
for(int i=0;i<=n;i++){
if(i==0&&j==0&&k==0)continue;
double &v=f[i][j][k];
if(i!=0)v+=f[i-1][j][k]*i/(i+j+k);
if(j!=0)v+=f[i+1][j-1][k]*j/(i+j+k);
if(k!=0)v+=f[i][j+1][k-1]*k/(i+j+k);
v+=1.0*n/(i+j+k);
}
}
}
cout<<setiosflags(ios::fixed)<<setprecision(14);
cout<<f[a[1]][a[2]][a[3]];
return 0;
}
Stones
我们设
可以发现,
所以转移方程为:当存在一个
代码:
#include<bits/stdc++.h>
#define int long long
#define N 105
#define K 100005
using namespace std;
int n,k,a[N],f[K];
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++){
if(i<a[j])continue;
if(f[i-a[j]]==0)f[i]=1;
}
}
if(f[k]==1)cout<<"First";
else cout<<"Second";
return 0;
}
Deque
我们设
如果是该先手取数:
如果是该后手取数:
提示4:区间
应该先枚举区间长度,再枚举左端点,然后根据这两个东西确定右端点。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 3005
using namespace std;
int n,a[N],f[N][N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int len=1;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
int c=n-len;
if(len==1){
if(c%2==0)f[i][j]=a[i];
else f[i][j]=-a[i];
continue;
}
if(c%2==0)f[i][j]=max(f[i+1][j]+a[i],f[i][j-1]+a[j]);
else f[i][j]=min(f[i+1][j]-a[i],f[i][j-1]-a[j]);
}
}
cout<<f[1][n];
return 0;
}
Candies
这是我们的第一道
我们设
然后就发现,这个东西的复杂度过高,所以要采取一些优化。
发现一个事情,我们慢的原因是每次都要求一次和,这个东西就是我们的瓶颈。
考虑对这个和式做前缀和,然后在转移的时候直接使用记录前缀和转移,这样就可以通过了。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 105
#define K 100005
#define mod 1000000007
using namespace std;
int n,k,a[N],f[N][K],sum[K];
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
f[0][0]=1;
for(int i=1;i<=n;i++){
sum[0]=f[i-1][0];
for(int j=1;j<=k;j++){
sum[j]=(sum[j-1]+f[i-1][j])%mod;
}
for(int j=0;j<=k;j++){
int pos=max(0ll,j-a[i]);
if(pos==0){
(f[i][j]+=sum[j])%=mod;
}
else{
(f[i][j]+=sum[j]-sum[pos-1])%=mod;
}
f[i][j]=(f[i][j]+mod)%mod;
}
}
cout<<f[n][k];
return 0;
}
Slimes
我们设
这里的
这里为什么要加上
代码:
#include<bits/stdc++.h>
#define int long long
#define N 405
using namespace std;
int n,a[N],sum[N],f[N][N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
memset(f,0x3f,sizeof f);
for(int len=1;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
if(len==1){
f[i][j]=0;
continue;
}
for(int k=i;k<j;k++){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
}
}
}
cout<<f[1][n];
return 0;
}
Matching
我们设
所谓状态压缩,一般是用二进制表示某个东西的有无(当然也可能用其他进制表示一些复杂的东西)。这里
于是有转移方程:
提示
:在表示一个集合且集合大小比较小时,可以用二进制状态压缩法表示。
提示
: __builtin_popcount(i)
是指二进制表示下有多少位是 。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 21
#define M 1<<N
#define mod 1000000007
#define pct __builtin_popcount
using namespace std;
int n,a[N][N],f[M];
signed main(){
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>a[i][j];
}
}
f[0]=1;
for(int i=1;i<1<<n;i++){
int k=pct(i)-1;
for(int j=0;j<n;j++){
if((i>>j&1)&&(a[j][k]==1)){
(f[i]+=f[i-(1<<j)])%=mod;
}
}
}
cout<<f[(1<<n)-1];
return 0;
}
Independent Set
我们设
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
#define M N<<1
#define mod 1000000007
using namespace std;
int n,h[N],e[M],ne[M],idx,f[N][2];
void add(int a,int b){
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
void dfs(int u,int fa){
f[u][0]=f[u][1]=1;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==fa)continue;
dfs(j,u);
(f[u][0]*=f[j][1])%=mod;
(f[u][1]*=(f[j][0]+f[j][1]))%=mod;
}
}
signed main(){
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);add(b,a);
}
dfs(1,0);
cout<<(f[1][0]+f[1][1])%mod;
return 0;
}
Flowers
首先引入一个东西,对于一个长度为
于是在
发现这样做有点慢,瓶颈在于遍历所有的
事实上,我们可以把
现在回到这道题,我们设
于是在
只需把
代码:
#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
int n,a[N],b[N],c[N],f[N];
int lowbit(int x){
return x&-x;
}
void modify(int x,int v){
while(x<=n){
c[x]=max(c[x],v);
x+=lowbit(x);
}
}
int qry(int x){
int res=0;
while(x){
res=max(res,c[x]);
x-=lowbit(x);
}
return res;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
f[i]=qry(a[i]-1)+b[i];
modify(a[i],f[i]);
}
int res=0;
for(int i=1;i<=n;i++){
res=max(res,f[i]);
}
cout<<res;
return 0;
}
Walk
我们设
于是有转移方程:
因为如果
但是我们发现如果这样做
观察一个东西,我们可以发现
提示
:在矩阵乘法中代表单位 的矩阵具有 且其他值全为 的特点。
代码:
#include<bits/stdc++.h>
#define int long long
#define N 55
#define mod 1000000007
using namespace std;
int n,k;
struct node{
int f[N][N];
}a;
node operator*(node a,node b){
node res;
memset(res.f,0,sizeof res.f);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
(res.f[i][j]+=a.f[i][k]*b.f[k][j]%mod)%=mod;
}
}
}
return res;
}
node ksm(node x,int y){
node res;
memset(res.f,0,sizeof res.f);
for(int i=1;i<=n;i++){
res.f[i][i]=1;
}
while(y){
if(y&1)res=res*x;
x=x*x;
y>>=1;
}
return res;
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a.f[i][j];
}
}
node b=ksm(a,k);
int res=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
(res+=b.f[i][j])%=mod;
}
}
cout<<res;
return 0;
}
Digit Sum
我们考虑设
于是我们可以使用记忆化搜索转移。转移方程:
这里
转移边界为
代码:
#include<bits/stdc++.h>
#define int long long
#define N 10005
#define D 105
#define mod 1000000007
using namespace std;
int d,num[N],cnt,f[N][D][2];
string k;
int dfs(int dep,int sum,bool is_lim){
if(dep==0)return sum==0;
int &v=f[dep][sum][is_lim];
if(v!=-1)return v;
int lim=9;
if(is_lim)lim=num[dep];
int res=0;
for(int i=0;i<=lim;i++){
(res+=dfs(dep-1,(sum+i)%d,is_lim&&(i==lim)))%=mod;
}
return v=res;
}
int solve(){
for(int i=k.size()-1;i>=0;i--){
num[++cnt]=k[i]-'0';
}
return dfs(cnt,0,1);
}
signed main(){
memset(f,-1,sizeof f);
cin>>k>>d;
int res=solve()-1;
res=(res+mod)%mod;
cout<<res;
return 0;
}
Permutation
我们设
于是我们分类讨论得出转移方程:
当字符为 <
,
当字符为 >
,
不难发现又是使用一个和式转移,所以我们对
代码:
#include<bits/stdc++.h>
#define int long long
#define N 3005
#define mod 1000000007
using namespace std;
int n,f[N][N],sum[N];
char s[N];
signed main(){
cin>>n;
for(int i=2;i<=n;i++){
cin>>s[i];
}
f[1][1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
sum[j]=(sum[j-1]+f[i-1][j])%mod;
}
for(int j=1;j<=i;j++){
if(s[i]=='<'){
(f[i][j]+=sum[j-1])%=mod;
}
else{
(f[i][j]+=sum[i-1]-sum[j-1])%=mod;
}
f[i][j]=(f[i][j]+mod)%mod;
}
}
int res=0;
for(int i=1;i<=n;i++){
(res+=f[n][i])%=mod;
}
cout<<res;
return 0;
}
Grouping
我们设
这里
然后就是,枚举子集可以用位运算表示,复杂度
代码:
#include<bits/stdc++.h>
#define int long long
#define N 16
#define M 1<<N
using namespace std;
int n,f[M],val[M],a[N][N];
signed main(){
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>a[i][j];
}
}
for(int i=0;i<1<<n;i++){
for(int j=0;j<n;j++){
if(!(i>>j&1))continue;
for(int k=j+1;k<n;k++){
if(!(i>>k&1))continue;
val[i]+=a[j][k];
}
}
f[i]=val[i];
}
for(int i=0;i<1<<n;i++){
for(int j=i;j;j=j-1&i){
f[i]=max(f[i],f[j]+val[i-j]);
}
}
cout<<f[(1<<n)-1];
return 0;
}
Subtree
我们设
于是对于
设
如果我们暴力找
我们可以对于每个
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int n,mod,f[N],g[N],t1[N],t2[N];
vector<int>e[N];
void add(int a,int b){
e[a].push_back(b);
}
void dfs1(int u,int fa){
f[u]=1;
vector<int>son;
for(int j:e[u]){
if(j==fa)continue;
dfs1(j,u);
(f[u]*=(f[j]+1))%=mod;
son.push_back(j);
}
int p1=1,p2=1;
for(int j:son){
t1[j]=p1;
(p1*=(f[j]+1))%=mod;
}
reverse(son.begin(),son.end());
for(int j:son){
t2[j]=p2;
(p2*=(f[j]+1))%=mod;
}
}
void dfs2(int u,int fa){
if(fa==0)g[u]=1;
else g[u]=(g[fa]*t1[u]%mod*t2[u]%mod+1)%mod;
for(int j:e[u]){
if(j==fa)continue;
dfs2(j,u);
}
}
signed main(){
cin>>n>>mod;
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);add(b,a);
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++){
cout<<f[i]*g[i]%mod<<'\n';
}
return 0;
}
Intervals
我们先考虑一个朴素的 1
放在
于是有转移方程:
空间是好优化的,直接把
接下来考虑时间上的优化。可以发现每一次都是一个区间的
于是,我们先把所有区间按照
代码:
#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
int n,m,f[N<<2],lzy[N<<2];
struct node{
int l,r,v;
bool operator<(const node &t)const{
return r<t.r;
}
}a[N];
void pushup(int u){
f[u]=max(f[u<<1],f[u<<1|1]);
}
void pushdown(int u){
f[u<<1]+=lzy[u];
f[u<<1|1]+=lzy[u];
lzy[u<<1]+=lzy[u];
lzy[u<<1|1]+=lzy[u];
lzy[u]=0;
}
void modify(int u,int l,int r,int L,int R,int v){
if(l>=L&&r<=R){
f[u]+=v;
lzy[u]+=v;
return;
}
int mid=l+r>>1;
pushdown(u);
if(L<=mid)modify(u<<1,l,mid,L,R,v);
if(R>mid)modify(u<<1|1,mid+1,r,L,R,v);
pushup(u);
}
int qry(){
return max(f[1],0ll);
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a[i].l>>a[i].r>>a[i].v;
}
sort(a+1,a+m+1);
int j=1;
for(int i=1;i<=n;i++){
modify(1,1,n,i,i,qry());
while(j<=m&&a[j].r==i){
modify(1,1,n,a[j].l,a[j].r,a[j].v);
j++;
}
}
cout<<qry();
return 0;
}
Tower
考虑
于是我们按照这个排个序,然后写一个背包就做完了。
具体地,设
代码:
#include<bits/stdc++.h>
#define int long long
#define N 1005
#define M 20005
using namespace std;
int n,f[M];
struct node{
int w,s,v;
bool operator<(const node &t)const{
return w+s<t.w+t.s;
}
}a[N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].w>>a[i].s>>a[i].v;
}
sort(a+1,a+n+1);
int res=0;
for(int i=1;i<=n;i++){
for(int j=a[i].w+a[i].s;j>=a[i].w;j--){
f[j]=max(f[j],f[j-a[i].w]+a[i].v);
res=max(res,f[j]);
}
}
cout<<res;
return 0;
}
Grid 2
首先之前提到过一个结论,对于没有障碍的网格,从
于是我们设
接下来,我们找到所有走到第
最后就是把终点当作第
代码:
#include<bits/stdc++.h>
#define int long long
#define N 3005
#define M 200005
#define mod 1000000007
using namespace std;
int h,w,n,fac[M],inv[M],f[N];
struct node{
int x,y;
bool operator<(const node &t)const{
if(x==t.x)return y<t.y;
return x<t.x;
}
}a[N];
int ksm(int x,int y){
int res=1;
while(y){
if(y&1)(res*=x)%=mod;
(x*=x)%=mod;
y>>=1;
}
return res;
}
void init(){
fac[0]=1;
inv[0]=1;
for(int i=1;i<M;i++){
fac[i]=fac[i-1]*i%mod;
inv[i]=ksm(fac[i],mod-2);
}
}
int c(int n,int m){
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main(){
init();
cin>>h>>w>>n;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
}
a[++n]={h,w};
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
f[i]=c(a[i].x+a[i].y-2,a[i].x-1);
for(int j=1;j<i;j++){
if(a[j].y>a[i].y)continue;
f[i]-=f[j]*c(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%mod;
f[i]=(f[i]+mod)%mod;
}
}
cout<<f[n];
return 0;
}
Frog 3
我们设
首先给出方程:
我们把它列出来:
我们假设
注意
这里我们可以把
代码:
#include<bits/stdc++.h>
#define int long long
#define N 200005
using namespace std;
int n,c,h[N],q[N],f[N];
double get(int i,int j){
return ((f[i]+h[i]*h[i])-(f[j]+h[j]*h[j]))*1.0/(h[i]-h[j]);
}
signed main(){
cin>>n>>c;
for(int i=1;i<=n;i++){
cin>>h[i];
}
int hh=0,tt=0;
q[tt]=1;
for(int i=2;i<=n;i++){
while(hh<tt&&get(q[hh],q[hh+1])<2*h[i])hh++;
f[i]=f[q[hh]]+(h[i]-h[q[hh]])*(h[i]-h[q[hh]])+c;
while(hh<tt&&get(q[tt-1],q[tt])>get(q[tt],i))tt--;
q[++tt]=i;
}
cout<<f[n];
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!