[做题记录-乱做]Luogu 3780 [SDOI2017]苹果树
首先一个很聚的转化是把\(t - h \leq k\)转化为先在一条链上不带代价地选择一个, 然后有代价地进行一堆树的dp。
根据贪心的性质, 这个链肯定到底。然后你再去搞出度的dfs序, 也就是出来的时候记录一个点。那么树dp可以转化到这个序列上的一个dp。
具体来说, 转移是\(dp_{i, j} = \max \{dp_{i - sz_i, j}, dp_{i - 1, j - t} + vi \times t \}\)。
然后你发现如果这样搞, 把一个点拆成两个, 一个用来当链, 贡献一个, 一个用来贡献剩下的, 然后把用来贡献的那个点的爸爸设置为原来那个点。发现把边表反过来和正着跑可以恰好构成贡献。那么单调队列优化一下dp然后正反拼拼贡献即可。
/*
QiuQiu /qq
____ _ _ __
/ __ \ (_) | | / /
| | | | _ _ _ | | _ _ / / __ _ __ _
| | | | | | | | | | | | | | | | / / / _` | / _` |
| |__| | | | | |_| | | | | |_| | / / | (_| | | (_| |
\___\_\ |_| \__,_| |_| \__, | /_/ \__, | \__, |
__/ | | | | |
|___/ |_| |_|
*/
#include <bits/stdc++.h>
using namespace std;
class Input {
#define MX 1000000
private :
char buf[MX], *p1 = buf, *p2 = buf;
inline char gc() {
if(p1 == p2) p2 = (p1 = buf) + fread(buf, 1, MX, stdin);
return p1 == p2 ? EOF : *(p1 ++);
}
public :
Input() {
#ifdef Open_File
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
#endif
}
template <typename T>
inline Input& operator >>(T &x) {
x = 0; int f = 1; char a = gc();
for(; ! isdigit(a); a = gc()) if(a == '-') f = -1;
for(; isdigit(a); a = gc())
x = x * 10 + a - '0';
x *= f;
return *this;
}
inline Input& operator >>(char &ch) {
while(1) {
ch = gc();
if(ch != '\n' && ch != ' ') return *this;
}
}
inline Input& operator >>(char *s) {
int p = 0;
while(1) {
s[p] = gc();
if(s[p] == '\n' || s[p] == ' ' || s[p] == EOF) break;
p ++;
}
s[p] = '\0';
return *this;
}
#undef MX
} Fin;
class Output {
#define MX 1000000
private :
char ouf[MX], *p1 = ouf, *p2 = ouf;
char Of[105], *o1 = Of, *o2 = Of;
void flush() { fwrite(ouf, 1, p2 - p1, stdout); p2 = p1; }
inline void pc(char ch) {
* (p2 ++) = ch;
if(p2 == p1 + MX) flush();
}
public :
template <typename T>
inline Output& operator << (T n) {
if(n < 0) pc('-'), n = -n;
if(n == 0) pc('0');
while(n) *(o1 ++) = (n % 10) ^ 48, n /= 10;
while(o1 != o2) pc(* (--o1));
return *this;
}
inline Output & operator << (char ch) {
pc(ch); return *this;
}
inline Output & operator <<(const char *ch) {
const char *p = ch;
while( *p != '\0' ) pc(* p ++);
return * this;
}
~Output() { flush(); }
#undef MX
} Fout;
#define cin Fin
#define cout Fout
#define endl '\n'
using LL = long long;
const int N = 4e4 + 5;
const int K = 5e5 + 5;
const int NK = 25000000 + 5;
vector<int> e[N];
int n, k, fa[N], a[N], v[N], sz;
vector<int> dp1[N];
vector<int> dp2[N];
int ListVal[N];
int dfn1[N], dfn2[N], siz[N], loc1[N], loc2[N], dep[N];
void clr() {
for(int i = 1; i <= sz; i ++) e[i].clear();
for(int i = 0; i <= sz; i ++) {
vector<int> a, b;
dp1[i].swap(a), dp2[i].swap(b);
}
dfn1[0] = dfn2[0] = 0;
}
void dfs1(int x, int fx = 0) {
siz[x] = 1; dep[x] = dep[fx] + 1;
ListVal[x] = ListVal[fa[x]] + v[x];
for(int y : e[x]) dfs1(y, x), siz[x] += siz[y];
dfn1[x] = ++ dfn1[0];
loc1[dfn1[0]] = x;
}
void dfs2(int x) {
for(int y : e[x]) dfs2(y);
dfn2[x] = ++ dfn2[0];
loc2[dfn2[0]] = x;
}
inline int max(int x, int y) { return x > y ? x : y; }
inline void dapai(vector<int> *dp, int *dfn, int *loc) {
// for(int i = 0; i <= sz; i ++)
// for(int j = 0; j <= k; j ++) dp[i][j] = 0;
for(int i = 1; i <= sz; i ++) {
int x = loc[i];
for(int j = 0; j <= k; j ++) {
dp[i][j] = dp[i - siz[x]][j];
}
static int q[K];
register int l = 1, r = 0;
// deque<int> q; q.push_back(0);
q[++ r] = 0;
dp[i][0] = 0;
for(register int j = 1; j <= k; j ++) {
while(l <= r && j - q[l] > a[x]) l ++;
//if(l <= r)
dp[i][j] = max(dp[i][j], dp[i - 1][q[l]] + v[x] * (j - q[l]));
while(l <= r && dp[i - 1][j] > dp[i - 1][q[r]] + (j - q[r]) * v[x]) r --;
q[++ r] = j;
}
}
}
void solve() {
cin >> n >> k;
for(int i = 1; i <= n; i ++) {
cin >> fa[i] >> a[i] >> v[i];
}
sz = n;
static int vis[N];
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i ++) {
if(fa[i])
e[fa[i]].push_back(i);
vis[fa[i]] = 1;
if(a[i] > 1) {
sz ++;
a[sz] = a[i] - 1;
v[sz] = v[i];
a[i] = 1;
e[i].push_back(sz);
fa[sz] = fa[i];
}
}
dfs1(1);
for(int i = 1; i <= sz; i ++) reverse(e[i].begin(), e[i].end());
dfs2(1);
for(int i = 0; i <= sz; i ++) dp1[i].resize(k + 1);
for(int i = 0; i <= sz; i ++) dp2[i].resize(k + 1);
dapai(dp1, dfn1, loc1);
dapai(dp2, dfn2, loc2);
int ans = 0;
for(int i = 1; i <= n; i ++) if(vis[i] == 0) {
for(int j = 0; j <= k; j ++) {
ans = max(ans, ListVal[i] + dp1[dfn1[i] - 1][j] + dp2[dfn2[i] - siz[i]][k - j]);
}
}
cout << ans << endl;
clr();
}
int main() {
//freopen("a.in", "r", stdin);
int Case; cin >> Case;
while(Case --) solve();
return 0;
}