【NOI2009】二叉查找树
题面
https://www.luogu.org/problem/P1864
题解
首先离散化优先级。
首先按权值排序,得到$dfs$序,一棵子树对于$dfs$序上一段区间,区间$dp$的经典模型。
设$F[k][l][r]$为对于$[l..r]$的元素,它们形成一棵二叉查找树,并且根节点的优先级$>k$的最小代价。(这样就可以接上一个优先级为$k$的根了)
转移时,枚举根节点(中间点),我们只考虑修改根节点的优先级,
不用修改时($a[d].p>k$),有$$f[k][l][r]=min(f[k][l][r],f[a[d].p][l][d-1]+f[a[d].p][d+1][r]+sum[r]-sum[l-1])$$
注意这里不能直接写成$f[k][l][r]=min(f[k][l][r],f[k][l][d-1]+f[k][d+1][r]+sum[r]-sum[l-1])$,因为这样写相当于不付任何代价的直接把$d$的优先级变大了,会对后面的值产生影响。
正确的式子本质上是一个和之前求的取$min$的过程。
需要修改时(一般情况)
$$f[k][l][r]=min(f[k][l][r],f[k][l][d-1]+f[k][d+1][r]+sum[r]-sum[l-1]+kk)$$
写这道题的时候我逻辑有些混乱,自己梳理一遍才行。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ri register int #define N 75 using namespace std; inline int read() { int ret=0,f=0; char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') ret*=10,ret+=ch-'0',ch=getchar(); return f?-ret:ret; } struct node { int v,p,w; bool operator < (const node &rhs) const { return v<rhs.v; } } a[N]; int n,kk,b[N],sum[N]; int f[N][N][N]; int main() { n=read(); kk=read(); for (ri i=1;i<=n;i++) a[i].v=read(); for (ri i=1;i<=n;i++) a[i].p=read(); for (ri i=1;i<=n;i++) a[i].w=read(); sort(a+1,a+n+1); for (ri i=1;i<=n;i++) b[i]=a[i].p; sort(b+1,b+n+1); int t=unique(b+1,b+n+1)-b-1; for (ri i=1;i<=n;i++) a[i].p=lower_bound(b+1,b+t+1,a[i].p)-b; for (ri i=1;i<=n;i++) sum[i]=sum[i-1]+a[i].w; memset(f,0x3f,sizeof(f)); for (ri i=0;i<=n;i++) for (ri k=0;k<=t;k++) f[k][i+1][i]=0; for (ri i=1;i<=n;i++) for (ri k=0;k<=t;k++) if (a[i].p>k) f[k][i][i]=a[i].w; else f[k][i][i]=a[i].w+kk; for (ri len=1;len<n;len++) for (ri l=1;l+len<=n;l++) { int r=l+len; for (ri k=t;k>=0;k--) { for (ri d=l;d<=r;d++) { if (a[d].p>k) f[k][l][r]=min(f[k][l][r],f[a[d].p][l][d-1]+f[a[d].p][d+1][r]+sum[r]-sum[l-1]); f[k][l][r]=min(f[k][l][r],f[k][l][d-1]+f[k][d+1][r]+sum[r]-sum[l-1]+kk); } } } cout<<f[0][1][n]<<endl; return 0; }