【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;
}

 

posted @ 2019-11-05 10:51  HellPix  阅读(144)  评论(0编辑  收藏  举报