link
#749. 「NOIP2021模拟赛 By ZYQ A」你就是方新童学长吗
sol
这里要用到笛卡尔树,所以想了好久都没想到正解
我们发现笛卡尔树有着非常优秀的性质,父节点的权值都比儿子大,左右儿子分别在序列两侧,所以和这道题的性质非常符合
我们将序列建一颗笛卡尔树,然后考虑 \(DP\)
定义 \(F[x][k]\) 表示第 \(x\) 号节点,在以这个节点为根的子树内取了 \(k\) 个点的最小 \(C_k\)
然后考虑转移,枚举左右两个儿子的 \(k\) ,分别为 \(i,j\)
因为 \(a[x]\) 在左右儿子之间且比左右儿子的所有节点都大,所以无论怎么样,都要加上跨过这个点的代价,即 \(a[x]\times i\times j\)
然后考虑取这个点,加上的区间个数就是 \(i+j+1\) 代价都是 \(a[x]\) 那么总的代价就是 \(a[x]\times (i+j+1)\)
code
#include<bits/stdc++.h>
#define min(a,b) (a<b?a:b)
using namespace std;
typedef long long LL;
const int maxn=5e3+5;
const LL INF=0x3F3F3F3F3F3F3F3F;
struct node{
LL w;
int ls,rs,size;
}a[maxn];
int N;
LL F[maxn][maxn];
int st[maxn],top;
struct IO{
static const int S=1<<21;
char buf[S],*p1,*p2;int st[105],Top;
~IO(){clear();}
inline void clear(){fwrite(buf,1,Top,stdout);Top=0;}
inline void pc(const char c){Top==S&&(clear(),0);buf[Top++]=c;}
inline char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline IO&operator >> (char&x){while(x=gc(),x==' '||x=='\n'||x=='\r');return *this;}
template<typename T>inline IO&operator >> (T&x){
x=0;bool f=0;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f^=1;ch=gc();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=gc();
f?x=-x:0;return *this;
}
inline IO&operator << (const char c){pc(c);return *this;}
template<typename T>inline IO&operator << (T x){
if(x<0) pc('-'),x=-x;
do{st[++st[0]]=x%10,x/=10;}while(x);
while(st[0]) pc('0'+st[st[0]--]);return *this;
}
}fin,fout;
void dfs(int x){
a[x].size=1;
if(a[x].ls) dfs(a[x].ls),a[x].size+=a[a[x].ls].size;
if(a[x].rs) dfs(a[x].rs),a[x].size+=a[a[x].rs].size;
for(int i=0;i<=a[a[x].ls].size;i++)
for(int j=0;j<=a[a[x].rs].size;j++){
F[x][i+j]=min(F[x][i+j],F[a[x].ls][i]+F[a[x].rs][j]+a[x].w*i*j);
F[x][i+j+1]=min(F[x][i+j+1],F[a[x].ls][i]+F[a[x].rs][j]+a[x].w*i*j+a[x].w*(i+j+1));
}
}
inline void build(){
for(int i=1;i<=N;i++){
while(top&&a[st[top]].w<a[i].w) a[i].ls=st[top--];
if(top) a[st[top]].rs=i;
st[++top]=i;
}
return ;
}
int main(){
freopen("A.in","r",stdin);
freopen("A.out","w",stdout);
fin>>N;
for(int i=1;i<=N;i++) fin>>a[i].w;
for(int i=1;i<=N;i++)
for(int j=1;j<=N+1;j++) F[i][j]=INF;
build();
dfs(st[1]);
for(int i=1;i<=N;i++) fout<<F[st[1]][i]<<'\n';
return 0;
}