题目链接:
https://mp.csdn.net/console/editor/html?not_checkout=1
题目大意:
给你一颗树,强盗要从树的根节点(节点1)出发,每个节点会有一些市民
每回合,每个市民们可以从当前节点跑到其子节点上,每个市民跑完之后,
强盗会选择其所在当前节点的一个子节点,像其移动。当强盗到一个叶节点上时
其会抓住改节点的所有市民。
强盗想抓住最多的市民
每个市民都想让强盗抓住的市民总数最少
现如果强盗按照最聪明的方式移动,市民们也按照最聪明的方式移动,求最终强盗能
抓住多少市民
输入与输出:
input:
一个n代表给定树有n个节点
接下来一行有n-1(2~n)个数字ai,代表第i个节点的父亲为ai
接下来一行有n个数字bi,代表第i个节点一开始有bi个市民
(2 <= n <= 2e5, 0 <= bi <= 2e5)
output:
一个数,代表强盗抓住的市民数量
思路:
这道题的题意比较绕,其实可以转化为一开始每个节点有bi个市民,
他们每个人都开始像叶节点走,问在各种走法中最后包含有最多的
市民的叶节点最少有可能为多少。
思路:
(1)复杂度nlogn:
我们可以先二分我们要求的答案,设我们每次二分到的答案为pres,
则,对于每一个叶节点,其最多能容纳pres的市民,而对于每一个非叶节点i
其能获得的容量为: 其所有(直接)子节点能贡献给他的容量
其需要的容量便为: bi
其能贡献给其父亲的容量为:其能获得的容量-bi
我们对于每次的二分结果都去验证一遍是否每个节点都能满足即可
(2)复杂度n:
设每个节点所对应的的子树所包含的叶节点为mi
设每个节点所对应的的子树所包含的市民数量为sum-bi
对于每个节点,其都有一种每个节点理想解(tres)<= 真实解(res)为:
向上取整(sum-bi / mi)
而我们对于每一个节点都求一个tres,然后取一个max便为res。
我们可以把tres分为两种:
1.不真实的tres:
因为其tres是让其子树所有节点的市民先走到他那里,但这是不可能的。
但因为tres < res 我们又是取的max,所有其实对答案没影响。
(图中绿色的为子树最优解,黄色的为当前节点的市民)
2.真实的tres:
如果tres是真是对答案有影响的,那么其tres = res的
代码:
(顶部的_runSt宏 为1用的是方法2, 为 0 用的是方法1)
#define _runSt 1
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 2e5 + 5;
typedef long long LL;
int xx[maxn], n;
LL dp[maxn][2], res;
vector<vector<int>> vec(maxn);
void dfs(int ii, int fa)
{
bool isF = 1;
dp[ii][0] = xx[ii];
for (int i = 0; i < vec[ii].size(); i++) {
isF = 0;
int e = vec[ii][i];
dfs(e, ii);
dp[ii][0] += dp[e][0];
dp[ii][1] += dp[e][1];
}
if (isF) dp[ii][1] = 1;
res = max(res, dp[ii][0] / dp[ii][1] + bool(dp[ii][0] % dp[ii][1]));
}
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; i++) {
int x;
scanf("%d", &x);
vec[x].push_back(i);
}
for (int i = 1; i <= n; i++)
scanf("%d", &xx[i]);
dfs(1, 1);
printf("%lld\n", res);
return 0;
}
#if _runSt
#else
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 2e5 + 5;
typedef long long LL;
vector<vector<int>> vec(maxn);
int n, numsLv, xx[maxn], out[maxn];
LL yy[maxn];
LL MAX_CC = 1e15, findcc;
bool canThis;
LL dfs(int ii, int fa)
{
LL thiscc = 0;
bool isL = 1;
for (int i = 0; i < vec[ii].size(); i++) {
isL = 0;
int e = vec[ii][i];
thiscc += dfs(e, ii);
if (thiscc > MAX_CC) thiscc = MAX_CC;
if (!canThis) return -1;
}
if (isL) thiscc = findcc;
//if (findcc == 2) {
// printf("xx[%d]=%d fa=%d thiscc=%lld\n", ii, xx[ii], fa, thiscc);
//}
if (thiscc >= xx[ii])
return thiscc - xx[ii];
else canThis = 0;
return -1;
}
bool chk(LL mm) {
canThis = 1;
findcc = mm;
dfs(1, 1);
return canThis;
}
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; i++) {
int x;
scanf("%d", &x);
vec[x].push_back(i);
}
for (int i = 1; i <= n; i++)
scanf("%d", &xx[i]);
LL lw = 0, hg = MAX_CC, res = -1;
while(lw <= hg){
LL mm = (lw + hg) >> 1;
if (chk(mm)) hg = mm - 1, res = mm;
else lw = mm + 1;
}
printf("%lld\n", res);
return 0;
}
#endif