CodeForces 1436 D. Bandit in a City 二分+卡常
题目大意:
给定一个 n n n 个节点的树,树的根节点一定是 1 1 1 ,每个节点都有权值 a i a_i ai,每个节点的权值都要往下分配转移,直到叶子节点,问叶子节点中最大的权值最小是多少.
权值可以任意整数分配,但一定要分完。
思路:
最大最小问题,直接考虑二分,二分节点的最大值, c h e c k check check 就是跑一遍树,但这题只有 1 s 1s 1s,所以可能卡常
解释下各数组的含义:
- n u m num num 数组表示当前节点可分配的权值
- s u m sum sum 数组表示以当前节点为根的子树的权值和
- c n t cnt cnt 数组表示以当前节点为根的子树的叶子节点个数
代码
#include <bits/stdc++.h>
using namespace std;
#define me(a, b) memset(a, b, sizeof(a))
#define IOS() ios::sync_with_stdio(false), cin.tie(0)
#define endl '\n'
typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 5;
const ll mod = 1e9 + 7;
vector<int> G[maxn];
ll sum[maxn];
ll cnt[maxn];
ll num[maxn];
void dfs(const int& u) {
if(G[u].empty()) {
++cnt[u];
return ;
}
ll s = 0;
for(const int& v : G[u]) {
dfs(v);
cnt[u] += cnt[v];
s += sum[v];
}
sum[u] += s;
}
bool check(const ll& x, const int& u) {
ll s = num[u];
bool res = true;
for(const int& v : G[u]) {
ll d = min(cnt[v]*x - sum[v], s);
if(d < 0)
return false;
s -= d;
sum[v] += d;
num[v] += d;
res = check(x, v);
sum[v] -= d;
num[v] -= d;
if(!res) return false;
}
return s == 0 || G[u].empty();
}
int main()
{
IOS();
int n;
cin >> n;
for(int i = 2; i <= n; ++i) {
int u;
cin >> u;
G[u].push_back(i);
}
for(int i = 1; i <= n; ++i) {
cin >> sum[i];
num[i] = sum[i];
}
dfs(1);
ll ans = 0;
ll l = sum[1]/cnt[1], r = sum[1];
while(l <= r) {
ll m = l + r >> 1;
if(check(m, 1)) {
ans = m;
r = m-1;
}
else
l = m + 1;
}
cout << ans << endl;
return 0;
}
总结:
跑了 982 m s 982ms 982ms,偷鸡过。