戳我
我剛看到這道題時無從下手,連\(dp\)狀態都不知道怎麼設。。。
於是墜入題解的深淵
根據定義,該二叉查詢樹中每個結點的資料值都比它左兒子結點的資料值大,而比它右兒子結點的資料值小。
則因資料值不會被修改,所以樹的中序遍歷不變(先左再中後右)。
於是我們可以在樹的中序遍歷上進行區間\(dp\),乙個區間就能代表一顆子樹。
又因為是否加上\(k\)代價取決於子樹根結點與其祖先結點的關係(權值不能比祖先小,否則你把哪個點當整個樹的根都可以),我們應該在狀態中維護該子樹根節點的值。即狀態為\(f[l][r][w]\)表示在\(l\)到\(r\)區間內,根節點權值為\(w\)的子樹的訪問代價。
於是就可以列轉移方程式:(\(s[i]\)是訪問頻率字首和)
\[if(a[i].w>=w)\\
f[l][r][w]=min(ans,f[l][i-1][a[i].w]+f[i+1][r][a[i].w]+s[r]-s[l-1])\]
\[f[l][r][w]=min(ans,f[l][i-1][w]+f[i+1][r][w]+s[r]-s[l-1]+k)
\]又注意到\(w\)資料範圍似乎很大(\(4*10^5\)),由於權值只要比較相對大小就可以了,直接離散化(然後我的離散化因為一開始不排序而gg!!!詳見**注釋部分)。
我選擇了寫著更舒服的記搜。。。
#include#include#include#include#include#include#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
struct node
il ll dfs(re int l,re int r,re int w)
return dp[l][r][w]=ans;
}int main()
NOI2009 二叉查詢樹
給定一棵嚴格的treap,父親節點的優先順序必然小於兒子節點的。權值按照二叉樹的定義,左兒子小於父親小於右兒子。深度從1開始定義,每個點除優先順序 數值之外,還有乙個訪問頻度。訪問頻度所產生的代價是 訪問頻度 該點深度 這和事實相符 可以用給定的k的代價,修改任意個點的優先順序為任意實數 當然,修改...
NOI2009 二叉查詢樹
給出乙個 n 個節點的二叉排序樹的原始資料值 d i 權值 v i 和訪問頻度 s i 你可以根據需要把結點的權值改為任何實數,但每次修改你會付出 k 的額外修改代價,且修改後所有結點的權值必須仍保持互不相同。求整棵樹的訪問代價與額外修改代價的最小和。n le 70,1 le k le 3 10 7...
bzoj1564 NOI2009 二叉查詢樹
首先先說明乙個坑點,這裡說數不能重複,但是數又可以取全體實數,而且修改代價又和數沒有關係,那麼我們其實可以直接看成整數的。然後我們是知道中序遍歷的 所以我們可以區間dp一下,設f i j k 表示i j建成一棵子樹,根的大小大於 否則值不變的時候只會影響1個點,答案沒有單調性求值複雜,還要多乙個n的...