bzoj2286 消耗戰 虛樹 樹形dp

2021-08-09 04:02:46 字數 1608 閱讀 5250

解題思路:

假如只有一次詢問,可以很好想到樹形dp方程:

如果u是關鍵點,則f[u]=w(fa[u],u);

如果u不是關鍵點,則f[u]=min(w(fa[u],u),

σ f[son[u]]);

但這樣一次複雜度為o(n),則總複雜度為o(nm),顯然超時。

像這種每次詢問都給出多個關鍵點的題,應該建虛樹,即每次詢問都只把關鍵點及其兩兩之間的lca提出來另建一棵樹,可以證明其規模為o(ki

),然後在這棵虛樹上dp,那總複雜度就變為了o(σk

i )

≤ 500000,就可以過了。

新的dp方程為:

如果u是關鍵點,則f[u]=mi[u];

如果u不是關鍵點,則f[u]=min(mi[u],

σ f[son[u]]);

其中mi[u]表示從根節點到u所經歷的最小邊權。

具體建虛樹方法詳見**中的solve()函式,複雜度為o(ki

logk

i )。

所以總複雜度為o(∑k

ilog

∑ki )。

注意要開long long。

#include

#include

#include

#include

#include

#include

#include

#define ll long long

using

namespace

std;

int getint()

const

int n=250005;

const ll inf=1e17;

int n,m,idx,cnt;

int dfn[n],dep[n],fa[n][20];

int top,stk[n],vir[n],par[n];

ll mi[n],f[n];

bool key[n];

bool cmp(const

int &a,const

int &b)

inline

void dfs (int u,ll val)

}inline

void dp(int u)

if(!key[u])f[u]=min(mi[u],res);

else f[u]=mi[u];

}}tr,vtr;

int lca(int x,int y)

void solve()

int lca=lca(stk[top],u);

while(dep[stk[top]]>dep[lca])

par[u]=lca,stk[++top]=u;

}vtr.tot=0;

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

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

sort(vir+1,vir+cnt+1,cmp);

int rt=vir[1];

//建虛樹過程。

vtr.dp(rt);

cout

<'\n';

}int main()

BZOJ 2286 消耗戰(虛樹)

因為聽說多校第一場的09可以用虛樹來做,於是趕緊趁著沒比賽補一發相關知識點。臥槽寫完這篇題解之後,突然發現多校09題解給的方法不就是維護一顆動態虛樹嘛!只不過說虛樹是動態的,你需要將點插入虛樹中,就要動態維護這個樹的權值。事實上虛樹就是通過重新建樹,使得樹上的 無用決策點 全部去掉 當然相關資訊必須...

BZOJ2286 消耗戰(虛樹,樹形dp)

今天本tu生日,學個新演算法慶祝一下。學了虛樹,碰到每次詢問給你一些點點的樹就不虛了 對於一棵樹,我們可以在上面用我們學過的演算法為所欲為。假設題目有多個詢問,每個詢問給出了一些點,那我們可以把這些點和及有關係的點拉出來,合併點和邊的資訊,構出虛樹,在虛樹上繼續為所欲為。對於與詢問有關係的點,就是這...

消耗戰 dp 虛樹

題目描述 在一場戰爭中,戰場由 n 個島嶼和 n 1 個橋梁組成,保證每兩個島嶼間有且僅有一條路徑可達。現在,我軍已經偵查到敵軍的總部在編號為 1 的島嶼,而且他們已經沒有足夠多的能源維繫戰鬥,我軍勝利在望。已知在其他 k 個島嶼上有豐富能源,為了防止敵軍獲取能源,我軍的任務是炸毀一些橋梁,使得敵軍...