給出乙個\(n\)個節點的二叉排序樹的原始資料值\(d[i]\)、權值\(v[i]\)和訪問頻度\(s[i]\),你可以根據需要把結點的權值改為任何實數,但每次修改你會付出\(k\)的額外修改代價,且修改後所有結點的權值必須仍保持互不相同。
求整棵樹的訪問代價與額外修改代價的最小和。
\[n\le 70,1\le k\le 3*10^7
\]根據最優二叉查詢樹的性質,考慮區間dp。
如果設\(f[i][j]\)表示僅考慮區間\([i,j]\)內的節點所能得到的最小代價
那麼有轉移方程
\[f[i][j]=\min_^^+[(\min_^)==v[p]]\times k}
\](定義\(f[x][x-1]=0\))
時間複雜度為\(o(n^3)\)
似乎太優秀了哈
交上去後驚喜地發現只有\(30'\).....
因為我們不僅需要考慮每一段區間合併成一棵二叉樹後的最小代價,
還需要關注其節點在不同權值情況下的最小代價
因為節點的代價會影響到後面的決策,使得設定的狀態不滿足最優子結構
考慮節點作為當前樹根被迫需要改變權值的情況只受到其子樹權值最小值的影響(這段話很長,請多讀幾遍)因此多加一維表示區間內節點的最小值時的子問題
即設\(f[i][j][k]\)表示僅考慮區間\([i,j]\)內的節點,
且使得當前所有節點的權值在離散化後都不小於\(k\)所能得到的最小代價
那麼決策便是改變權值(區間內有節點權值比當前列舉到的節點的權值要小)或者不改變權值(區間內所有節點權值都小於列舉到的節點的權值);
轉移方程在之前的方程中多加一維即可
時間複雜度為\(o(n^4)\)
#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#define mp make_pair
#define pb push_back
#define rg register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vectorvi;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e8;
const int n=75;
il ll read()
il void file()
struct nodet[n];
bool cmp(node a,node b)
int n,k,s[n],m[n][n],f[n][n];
int main()
NOI2009 二叉查詢樹
給定一棵嚴格的treap,父親節點的優先順序必然小於兒子節點的。權值按照二叉樹的定義,左兒子小於父親小於右兒子。深度從1開始定義,每個點除優先順序 數值之外,還有乙個訪問頻度。訪問頻度所產生的代價是 訪問頻度 該點深度 這和事實相符 可以用給定的k的代價,修改任意個點的優先順序為任意實數 當然,修改...
NOI2009 二叉查詢樹
戳我 我剛看到這道題時無從下手,連 dp 狀態都不知道怎麼設。於是墜入題解的深淵 根據定義,該二叉查詢樹中每個結點的資料值都比它左兒子結點的資料值大,而比它右兒子結點的資料值小。則因資料值不會被修改,所以樹的中序遍歷不變 先左再中後右 於是我們可以在樹的中序遍歷上進行區間 dp 乙個區間就能代表一顆...
bzoj1564 NOI2009 二叉查詢樹
首先先說明乙個坑點,這裡說數不能重複,但是數又可以取全體實數,而且修改代價又和數沒有關係,那麼我們其實可以直接看成整數的。然後我們是知道中序遍歷的 所以我們可以區間dp一下,設f i j k 表示i j建成一棵子樹,根的大小大於 否則值不變的時候只會影響1個點,答案沒有單調性求值複雜,還要多乙個n的...