嘟嘟嘟
首先這一眼看出來,要樹形dp。
然後發現狀態不好設,剛開始我想的是dp[i][j]表示以\(i\)為根的子樹,選了\(j\)個黑點的最大價值。結果就不會轉移了。
轉移的時候想考慮\(\)這一條邊的貢獻,但是發現這個狀態的轉移所涉及的不只是這一條邊,還有子樹中的邊,於是就徹底gg了。
還是看了題解。
題解也是考慮貢獻,而且也是考慮每一條邊,但最大的區別是,我們列舉邊,然後考慮邊兩側的點對答案的貢獻。
令dp[i][j]表示以\(i\)為根的子樹,選了\(j\)個黑點,已經列舉到第\(x\)條邊時,當前答案的最大值。
因為dfs的時候就相當於列舉邊了,所以這一維自然省去。
於是對於黑點,有\(j * (k - j) * w(u, v)\)的貢獻,
對於白點,有\((k - j) * (n - size[v] - (k - j)) * w(u, v)\)的貢獻。
最後記得要初始化dp陣列為-inf,因為有些狀態不合法。
#include#include#include#include#include#include#include#include#include#includeusing namespace std;
#define enter puts("")
#define space putchar(' ')
#define mem(a, x) memset(a, x, sizeof(a))
#define in inline
typedef long long ll;
typedef double db;
const int inf = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e3 + 5;
inline ll read()
while(isdigit(ch))
if(last == '-') ans = -ans;
return ans;
}inline void write(ll x)
int n, k;
struct edge
e[maxn << 1];
int head[maxn], ecnt = -1;
in void addedge(int x, int y, ll w)
; head[x] = ecnt;
}int siz[maxn];
ll dp[maxn][maxn];
in void dfs(int now, int _f)
}int main()
for(int i = 1; i <= n; ++i) fill(dp[i] + 1, dp[i] + k + 1, inf);
//帶勁的操作
dfs(1, 0);
write(dp[1][k]), enter;
return 0;
}
HAOI2015 樹上染色
有一棵點數為 n 的樹,樹邊有邊權。給你乙個在 0 n 之內的正整數k,你要在這棵樹中選擇 k 個點,將其染成黑色,並將其他的 n k個點染成白色。將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間距離的和的收益。問收益最大值是多少。輸入第一行兩個整數 n,k。接下來 n 1 行每行三個正整...
HAOI2015 樹上染色
考慮子樹當中所有邊的貢獻即可。然後就能簡單做樹上揹包了。但是要注意列舉的順序,應該從大到小更新,否則某個狀態會多次被加。如果不想考慮列舉順序,那麼直接dp的時候用乙個臨時陣列記錄。ac pragma gcc optimize ofast funroll all loops include defin...
HAOI2015 樹上染色
有一棵點數為 n 的樹,樹邊有邊權。給你乙個在 0 n 之內的正整數 k 你要在這棵樹中選擇 k個點,將其染成黑色,並將其他 的n k個點染成白色 將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間的距離的和的受益。問受益最大值是多少。輸入格式 第一行包含兩個整數 n,k 接下來 n 1 行...