「NOIP2021模拟赛 By ZYQ A」你就是方新童学长吗 题解

Link

#749. 「NOIP2021模拟赛 By ZYQ A」你就是方新童学长吗

Solution

这里要用到笛卡尔树,所以想了好久都没想到正解

我们发现笛卡尔树有着非常优秀的性质,父节点的权值都比儿子大,左右儿子分别在序列两侧,所以和这道题的性质非常符合

我们将序列建一颗笛卡尔树,然后考虑 \(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;
}
posted @ 2021-11-16 20:08  Martian148  阅读(47)  评论(0编辑  收藏  举报