CF1746D Paths On The Tree - dp - 贪心 - 记忆化搜索 -

题目链接:https://codeforces.com/contest/1746/problem/D

题解:
显然,对于某个点,如果此时经过该点的路径个数为\(t\),那么他的儿子的经过个数一定为\(t/|son|\)或者\(t/(|son|+1)\),直接枚举哪些位置+1是指数级别的,注意到总共有多少个+1是确定的,所以连01背包都不需要,直接贪心选即可,设\(dp[i][j]\)表示i结点,当前点经过的路径数为j的最大val
注意k很大,所以只能用一个map<int,map<int,int> >dp来存dp[][]
注意这样做会被链卡掉,判一下即可
除去链之后,每次路径数至少/2,所以是nlog级别的

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;
#define int LL

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+ 5;

int n,k;
int s[maxn];
vector<int>g[maxn];
map<int,map<int,int> >dp;

int cmp(pii a, pii b){return a.second - a.first > b.second - b.first;}

int dfs(int x,int sum){
	if(dp[x].count(sum))return dp[x][sum];
	if(g[x].size() == 0)return dp[x][sum] = sum * s[x];
	
	int bs = sum * s[x];
	int tots = g[x].size(), rem = sum % g[x].size();
	if(rem == 0){
		int r1 = 0;
		for(int u : g[x])r1 += dfs(u, sum / tots);
		return dp[x][sum] = r1 + bs;
	}
	vector<pair<int,int> >vc;
	int r1 = 0;
	for(int u : g[x]){
		int t1 = dfs(u, sum / tots);
		int t2 = dfs(u, sum / tots + 1);
		vc.push_back(mpr(t1, t2));
		r1 += t1;
	}
	sort(vc.begin(),vc.end(), cmp);
	for(int i=0;i<rem;i++)r1 += vc[i].second - vc[i].first;
	return dp[x][sum] = r1 + bs;
}

void solve(){
	scanf("%I64d%I64d",&n,&k);
	for(int i=1;i<=n;i++)g[i].clear(), dp[i].clear();
	for(int i=2;i<=n;i++){
		int p;scanf("%I64d",&p);
		g[p].push_back(i);
	}
	for(int i=1;i<=n;i++)scanf("%I64d",&s[i]);
	printf("%I64d\n", dfs(1, k));
	
}

signed main(){
	int te;scanf("%I64d",&te);
	while(te --)solve();

	return 0;
}
posted @ 2022-10-19 21:59  SkyRainWind  阅读(20)  评论(0编辑  收藏  举报