[bzoj1564]二叉查找樹
題目描述
已知一棵特殊的二叉查找樹。根據定義,該二叉查找樹中每個結點的數據值都比它左兒子結點的數據值大,而比它右兒子結點的數據值小。
另一方面,這棵查找樹中每個結點都有一個權值,每個結點的權值都比它的兒子結點的權值要小。
已知樹中所有結點的數據值各不相同;所有結點的權值也各不相同。這時可得出這樣一個有趣的結論:如果能夠確定樹中每個結點的數據值和權值,那麽樹的形態便可以唯一確定。因為這樣的一棵樹可以看成是按照權值從小到大順序插入結點所得到的、按照數據值排序的二叉查找樹。
一個結點在樹中的深度定義為它到樹根的距離加1。因此樹的根結點的深度為1。
每個結點除了數據值和權值以外,還有一個訪問頻度。我們定義一個結點在樹中的訪問代價為它的訪問頻度乘以它在樹中的深度。整棵樹的訪問代價定義為所有結點在樹中的訪問代價之和。
現在給定每個結點的數據值、權值和訪問頻度,你可以根據需要修改某些結點的權值,但每次修改你會付出K的額外修改代價。你可以把結點的權值改為任何實數,但是修改後所有結點的權值必須仍保持互不相同。現在你要解決的問題是,整棵樹的訪問代價與額外修改代價的和最小是多少?
輸入輸出格式
輸入格式:
輸入文件中的第一行為兩個正整數N,K。其中:N表示結點的個數,K表示每次修改所需的額外修改代價。
接下來的一行為N個非負整數,表示每個結點的數據值。
再接下來的一行為N個非負整數,表示每個結點的權值。
再接下來的一行為N個非負整數,表示每個結點的訪問頻度。
其中:所有的數據值、權值、訪問頻度均不超過400000。每兩個數之間都有一個空格分隔,且行尾沒有空格。
輸出格式:
輸出文件中僅一行為一個數,即你所能得到的整棵樹的訪問代價與額外修改代價之和的最小值。
輸入輸出樣例
輸入樣例#1:4 10
1 2 3 4
1 2 3 4
1 2 3 4
輸出樣例#1:29
說明
【樣例說明】
輸入的原圖是左圖,它的訪問代價是:1×1+2×2+3×3+4×4=30。
最佳的修改方案是把輸入中的第3個結點的權值改成0,得到右圖,訪問代價是:1×2+2×3+3×1+4×2=19,加上額外修改代價10,一共是29。
【數據規模和約定】
對於40%的數據,滿足:N<=30;
對於70%的數據,滿足:N<=50;
對於100%的數據,滿足:N<=70,1<=K<=30000000。
昨天的考試題。。。昨天不會,寫了個rand100000次的萎爛做法,竟然此題得到了零分的好成績!!!然後今天看了一遍題解。。。
和加分二叉樹有點像。。。一個區間DP,因為樹的中序遍歷一定確定,所以只需在一個區間裏枚舉根,然後統計答案。。。狀態就設f[i][j][e]為i~j的子樹而且子樹中所有權值都大於e的最小代價。。。每個f需要加上這裏面的訪問頻度,然後就可以一層一層加上去了。。。。。。
正確性的證明:不會
1 // It is made by XZZ 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 #define rep(a,b,c) for(rg int a=b;a<=c;a++) 7 #define drep(a,b,c) for(rg int a=b;a>=c;a--) 8 #define erep(a,b) for(rg int a=fir[b];a;a=nxt[a]) 9 #define il inline 10 #define rg register 11 #define vd void 12 13 typedef long long ll; 14 il int gi(){ 15 rg int x=0;rg char ch=getchar(); 16 while(ch<‘0‘||ch>‘9‘)ch=getchar(); 17 while(ch>=‘0‘&&ch<=‘9‘)x=x*10+ch-‘0‘,ch=getchar(); 18 return x; 19 } 20 struct node{ll d,w,n;}a[72]; 21 il bool cmp(node kk,node kkk){return kk.w<kkk.w;} 22 il bool cmp2(node kk,node kkk){return kk.d<kkk.d;} 23 ll f[77][77][77]; 24 ll num[77]; 25 int main(){ 26 rg int n=gi();rg ll K=gi(); 27 rep(i,1,n)a[i].d=gi(); 28 rep(i,1,n)a[i].w=gi(),num[i]=a[i].w; 29 rep(i,1,n)a[i].n=gi(); 30 sort(a+1,a+1+n,cmp2); 31 rep(i,1,n)a[i].n+=a[i-1].n; 32 sort(num+1,num+1+n); 33 int mn=unique(num+1,num+1+n)-num-1; 34 rep(i,1,n)a[i].w=lower_bound(num+1,num+1+mn,a[i].w)-num; 35 rep(siz,0,n-1)rep(i,1,n-siz)rep(e,1,mn){ 36 ll&now=f[i][i+siz][e];now=2333333333333333333ll; 37 rg int l=i,r=i+siz; 38 rep(k,l,r){ 39 if(a[k].w>=e)now=min(now,f[l][k-1][a[k].w]+f[k+1][r][a[k].w]+a[r].n-a[l-1].n); 40 now=min(now,K+f[l][k-1][e]+f[k+1][r][e]+a[r].n-a[l-1].n); 41 } 42 } 43 ll ans=f[1][n][1]; 44 rep(i,2,mn)ans=min(ans,f[1][n][i]); 45 printf("%lld\n",ans); 46 return 0; 47 }View Code
[bzoj1564]二叉查找樹