S T4 求调 用了两个二分和贪心 思路还是很清晰的 贪心思路是预处理该总天数下,每个节点最晚需要开始种树是第几天(f[i]),用一个优先队列维护每一个节点代表的子树中,需要最早开始种树的那个节点的是第几天(tr[i]=min(f[i])),优先队列维护队头tr[i]最小)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, b[MAXN], c[MAXN], u, v, l, r, ans, mid, f[MAXN], g[MAXN], tr[MAXN];
long long a[MAXN];
//f[i]表示该day下第i个节点最晚需要开始种树是第f[i]天
//g[i]表示第i个节点从第g[i]天开始每天只能长1米
//tr[i]表示第i个节点所在子树最小的f[i]值
vector<int> e[MAXN];
struct cmp
{
bool operator()(int a, int b)
{
return tr[a] > tr[b];
}
};
priority_queue<int, vector<int>, cmp> q;
void dfs(int u)
{
tr[u] = f[u];
for (int i = 0; i < e[u].size(); i++)
{
int v = e[u][i];
dfs(v);
tr[u] = min(tr[u], tr[v]);
}
return;
}
bool check(int day)
{
memset(f, 0x3f3f3f3f, sizeof(f));
memset(tr, 0x3f3f3f3f, sizeof(tr));
for (int i = 1; i <= n; i++)
{
int ll = 0, rr = day, anss = -1, midd;
long long x, y, add;
while (ll <= rr)//查找f[i]表示该day下第i个节点最晚需要开始种树是第f[i]天
{
midd = (ll+rr) >> 1;
if (g[i] <= midd)
{
add = day-midd+1;
}
else if (g[i] > midd && g[i] <= day)
{
x = b[i]+midd*c[i];//首项
y = b[i]+(g[i]-1)*c[i];//末项
add = (x+y)*(g[i]-midd)/2 + (day-g[i]+1);//等差数列
}
else
{
x = b[i]+midd*c[i];//首项
y = b[i]+day*c[i];//末项
add = (x+y)*(day-midd+1)/2;//等差数列
}
if (add >= a[i])//add=第mid天到第dayy天树长高的高度
{
ll = midd+1;
anss = midd;
}
else
rr = midd-1;
}
if (anss == -1)
return false;
f[i] = anss;
}
dfs(1);
while (!q.empty())
q.pop();
q.push(1);
int dayy = 0;
while (!q.empty())
{
++dayy;
int now = q.top();
q.pop();
for (int i = 0; i < e[now].size(); i++)
{
int v = e[now][i];
if (dayy > f[v])
return false;
q.push(v);
}
}
return true;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%ld%d%d", &a[i], &b[i], &c[i]);
if (c[i] >= 0)
g[i] = 0x3f3f3f3f;
else//b[i]<=x*-c[i]
g[i] = ceil(b[i] / (-c[i]));
}
for (int i = 1; i <= n-1; i++)
{
scanf("%d%d", &u, &v);
e[u].push_back(v);
}
l = n, r = 1e9;//最少天数n,最多1e9
while (l<=r)
{
mid = (l+r) << 1;
if (check(mid))
{
r = mid-1;
ans = mid;
}
else
l = mid+1;
}
cout << ans;
return 0;
}