NOI2009 二叉查詢樹

2022-02-06 04:26:37 字數 2635 閱讀 8943

給定一棵嚴格的treap,父親節點的優先順序必然小於兒子節點的。權值按照二叉樹的定義,左兒子小於父親小於右兒子。

深度從1開始定義,每個點除優先順序、數值之外,還有乙個訪問頻度。

訪問頻度所產生的代價是:訪問頻度*該點深度(這和事實相符)

可以用給定的k的代價,修改任意個點的優先順序為任意實數(當然,修改優先順序,樹的形態,各點深度就可能變化了)

最終的總代價為:頻度產生代價+修改代價。

最小化這個總代價。

n<=70,1<=k<=30000000

平衡樹是乙個動態的資料結構,難以抓住形態的變化,也不方便記錄深度之類。所以必須抓住不變的量當做突破口。

不管平衡樹怎麼轉,根據二叉樹的定義,它的中序遍歷一定是不變的。

所以我們可以找到這棵樹的中序遍歷,就把這棵樹變成了乙個靜態的區間,只不過每個區間所代表的點的優先順序可能會變。

發現,每乙個連續的子區間,都對應treap的連續一部分。可以把小的區間先建樹,再把大的區間用小的區間合併。我們合併的時候列舉的劃分點,就是這部分treap的樹根

區間dp順理成章。

除了f[l][r]之外,為了維護優先順序的關係,必然要再記錄一維。

發現,只要根節點的優先順序確定,子樹的優先順序的範圍就確定了。

所以考慮記錄根節點優先順序。(這裡優先順序只考慮相對大小,而且範圍又大,所以要離散化為1~n)

但是,樸素的f[l][r][w]中,w單單記錄根節點優先順序的話,由於子樹所有大於w的都可以轉移,還要for一遍。狀態n^3,轉移n^2,會爆。

所以,我們令f[l][r][w]表示,將l~r這段區間建成treap,其中根節點優先順序大於等於w的最小代價。

根據列舉的根節點是否修改,可以設計轉移方程是:

修改:f[l][r][w]=min(f[l][r][w],f[l][k-1][w]+f[k+1][r][w]+k+sum[r]-sum[l-1])  ————其中,sum[i]表示,區間中,1~i的訪問頻度和

當劃分點的優先順序ch[k]大於w時,可以不修改。

f[l][r][w]=min(f[l][r][w],f[l][k-1][ch[k]]+f[k+1][r][ch[k]]+sum[r]-suim[l-1])

最後答案就是:f[1][n][1];

注意,o迴圈的時候,必須倒序!!因為ch[k]>=o時候,要從o更大的地方獲取最小值,必須先把o較大的處理完。

#includeusing

namespace

std;

typedef

long

long

ll;const

int n=77

;const ll inf=2e18;

ll f[n][n][n];

intn;

ll m;

inta[n];

inttot;

intw[n],p[n],d[n];

intprio[n];

ll sum[n];

int ch[n];//

離散化後的優先順序

bool cmp(int a,int

b)int

main()

//l=1的初值

for(int i=1;i<=n;i++)

sum[i]=sum[i-1]+d[a[i]];//

字首和

for(int l=2;l<=n;l++)

for(int i=1;i<=n;i++)

}else

else

if(k==j)//

同理

else}}

}printf(

"%lld

",f[1][n][1

]);

return0;

}

太噁心了。為了保證l<=r,做出了巨大的討論。

其實不用這麼麻煩,只要讓l>r的時候,賦值為0就好,相當於不存在。根本不影響答案。

#includeusing

namespace

std;

typedef

long

long

ll;const

int n=77

;const ll inf=2e18;

ll f[n][n][n];

intn;

ll m;

inta[n];

inttot;

intw[n],p[n],d[n];

intprio[n];

ll sum[n];

int ch[n];//

離散化後的優先順序

bool cmp(int a,int

b)int

main()

//不放心,可以考慮代入長度小於等於2的情況。0的作用就出來了。

//連初始化l=1都省了。

printf("

%lld

",f[1][n][1

]);

return0;

}

總結:1.對於琢磨不透的變化,一定有不變的東西。一定要抓住其中的不變數,作為突破口。

2.迴圈順序要注意,乙個是不能有後效性,乙個是要保證能影響到這個狀態的所有狀態都處理完了。

3.注意考慮清楚所有可能轉移的方式。

NOI2009 二叉查詢樹

戳我 我剛看到這道題時無從下手,連 dp 狀態都不知道怎麼設。於是墜入題解的深淵 根據定義,該二叉查詢樹中每個結點的資料值都比它左兒子結點的資料值大,而比它右兒子結點的資料值小。則因資料值不會被修改,所以樹的中序遍歷不變 先左再中後右 於是我們可以在樹的中序遍歷上進行區間 dp 乙個區間就能代表一顆...

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的...