1564: [NOI2009]二叉查找树
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 879 Solved: 612
[Submit][Status][Discuss]
Description
Input
Output
只有一个数字,即你所能得到的整棵树的访问代价与额外修改代价之和的最小值。
Sample Input
4 10
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
Sample Output
29
HINT
输入的原图是左图,它的访问代价是1×1+2×2+3×3+4×4=30。最佳的修改方案是把输入中的第3个结点的权值改成0,得到右图,访问代价是1×2+2×3+3×1+4×2=19,加上额外修改代价10,一共是29。
Source
观察到这就是一个treap。(其实没什么卵用)
设sum为访问频率的前缀和。
考虑dp,设f[w][i][j]表示从i到j组成根节点为原权值第w小之后的点的最小总代价。
第一位从原来权值最大的点开始枚举w。
之后两维枚举i,j。
下一维枚举分割点d。
分两种情况讨论:
1.若修改点d的权值,那么f[w][i][j]=min(f[w][i][j],f[w][i][d-1]+f[w][d+1][to]+sum[to]-sum[i-1]+k);
2.若不修改点d的权值,那么点d的权值要大于m,f[w][i][j]=min(f[w][i][j],f[t[d].a][i][d-1]+f[t[d].a][d+1][to]+sum[to]-sum[i-1]);
ans=f[1][1][n]
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define maxn 80 8 using namespace std; 9 int read() { 10 int x=0,f=1;char ch=getchar(); 11 for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; 12 for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; 13 return x*f; 14 } 15 int n,k; 16 struct data { 17 int v,a,p; 18 }t[maxn]; 19 bool cmp1(data t1,data t2) {return t1.a<t2.a;} 20 bool cmp2(data t1,data t2) {return t1.v<t2.v;} 21 int sum[maxn]; 22 int f[maxn][maxn][maxn]; 23 int main() { 24 n=read();k=read(); 25 for(int i=1;i<=n;i++) t[i].v=read(); 26 for(int i=1;i<=n;i++) t[i].a=read(); 27 for(int i=1;i<=n;i++) t[i].p=read(); 28 sort(t+1,t+n+1,cmp1); 29 for(int i=1;i<=n;i++) t[i].a=i; 30 sort(t+1,t+n+1,cmp2); 31 for(int i=1;i<=n;i++) sum[i]=sum[i-1]+t[i].p; 32 for(int i=1;i<=n;i++) 33 for(int j=1;j<=n;j++) 34 if(t[i].a>=j) f[j][i][i]=t[i].p; 35 else f[j][i][i]=t[i].p+k; 36 for(int w=n;w>=1;w--) { 37 for(int j=1;j<=n;j++) { 38 for(int i=1;i+j<=n;i++) { 39 int to=i+j; 40 int tmp=2147483647; 41 for(int d=i;d<=to;d++) { 42 if(t[d].a>=w) tmp=min(tmp,f[t[d].a][i][d-1]+f[t[d].a][d+1][to]+sum[to]-sum[i-1]); 43 tmp=min(tmp,f[w][i][d-1]+f[w][d+1][to]+sum[to]-sum[i-1]+k); 44 } 45 f[w][i][to]=tmp; 46 } 47 } 48 } 49 printf("%d",f[1][1][n]); 50 }