[做题记录-乱做]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;
}
posted @ 2021-09-22 20:48  HN-wrp  阅读(29)  评论(0编辑  收藏  举报