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;
}