JSOI2018 潛入行動(樹形dp 小優化)

2022-05-01 04:57:10 字數 1270 閱讀 7070

直接樹形dp設\(f[i][j][0/1][0/1]\)表示\(i\)子樹,選了\(j\)個,\(i被覆蓋了嗎\),\(選了i嗎\)

複雜度是\(o(n*k^2)\)。

加上子樹大小優化,複雜度降為\(o(nk)\),還有其它優化:

考慮度數為1的點,與它唯一相鄰的點必須要選。

把必須要選的點找出來,如果\(>k\),則直接輸出0。

進行了這個優化後,發現要合併的\(>1\)的子樹不超過\(k\)個。

所以複雜度降為\(o(nk+k^3)\)

code:

#include#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)

#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)

#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)

#define ll long long

#define pp printf

#define hh pp("\n")

using namespace std;

const int mo = 1e9 + 7;

const int n = 1e5 + 5;

int n, k, x, y, r[n];

vectore[n];

#define pb push_back

#define si size()

int fa[n], siz[n];

int f[n][101][2][2];

ll g[101][2][2];

void dg(int x)

} siz[x] = min(k, siz[x] + siz[y]);

fo(i, 0, siz[x]) fo(j, 0, 1) fo(t, 0, 1)

f[x][i][j][t] = g[i][j][t] % mo; }}

int bz[n];

int main()

int cnt = 0;

fo(i, 1, n) if(r[i] == 1)

fo(i, 1, n) cnt += bz[i];

if(cnt > k)

dg(1);

ll ans = ((ll) f[1][k][1][0] + f[1][k][1][1]) % mo;

pp("%lld\n", ans);

}

JSOI2018 潛入行動

題目 我好菜啊,嚶嚶嚶 原來本地訪問陣列負下標不會報 re 或者 wa 甚至能跑出正解啊 這道題還是非常呆的 我們發現 k 很小,於是斷定這是乙個樹上揹包 發現在乙個點上安裝控制器並不能控制這個點,可能需要到父親那邊才能控制這個點,於是我們設 dp i j 0 1 0 1 表示在以 i 為根的子樹裡...

JSOI2018 潛入行動

一棵 n n le10 5 個結點的樹,在一些點上安裝 k k le min n,100 個裝置。每個裝置可以控制所有與安裝位置相鄰的結點 不包括本身 每個點可以安裝至多乙個裝置。問有多少種方案恰好用完 k 個裝置,使得所有的結點都被控制。樹形dp。f i j 0 1 0 1 表示以 i 為根的子樹...

JSOI2018 潛入行動

題目鏈結 參考題解 注意題面 在當前節點設定監聽裝置不能監聽到節點本身。那麼節點能否被覆蓋和節點是否設定監聽裝置需要分開設。設 f 表示以第 i 個節點為根的子樹,除了 i 以外的節點都已經被覆蓋,i 號節點有沒有設定監聽裝置 有沒有被覆蓋的方案數。轉移方程 f sum f f f sum f f ...