首先,第一眼可以看出二分答案(数据范围提示+单调性(即时间越长越好))
用如下代码实现:
int lft = 1,rgt = 1e9;
while(lft < rgt){
int mid = (lft + rgt) / 2;
if(check(mid))rgt = mid;
else lft = mid + 1;
}
下面具体探讨下check函数的写法:
注意到在任何情况下,每棵树每天都是会增高的!
容易想到利用每棵树长到特定高度
这一限定求出每棵树种植的deadline(最晚的日期)
deadline的求法:
__int128 sum(int pos,int l,int r){求出对于位于pos的树,在l天与r天之间的增高量
if(c[pos] >= 0 || b[pos] + c[pos] * r > 0){
return ((__int128)(r - l + 1)) * b[pos] + ((__int128)(c[pos])) * (l + r) * (r - l + 1) / 2;
}
else if(b[pos] + c[pos] * l <= 0) return r - l + 1;
else {
ll tmp = b[pos] / (-c[pos]);
if(b[pos] % c[pos] == 0)tmp--;
return (__int128)(tmp - l + 1) * b[pos] + (__int128)c[pos] * (l + tmp) * (tmp - l + 1) / 2 + r - tmp;
}
}
int calc_day(int pos,int Max){二分求出“刚好合适”的日期
int lft1 = 1,rgt1 = Max;
//cout << a[pos] << ' ' << b[pos] << ' ' << c[pos] << ' ' << (ll)sum(pos,1,Max) << endl;
if(sum(pos,1,Max) < a[pos])return -1;
while(lft1 < rgt1){
int mid = (lft1 + rgt1 + 1) / 2;
if(sum(pos,mid,Max) >= a[pos])lft1 = mid;
else rgt1 = mid - 1;
}
return lft1;
}
此时只需构造出前N棵树种植的最优顺序即可。
正向贪心明显存在后效性(只顾得上前面的节点,可能影响叶子节点)
反向考虑“最后一个”种谁?(前面的节点反正种上了,影响不到)
下面我们简单说明一下如此做法的可行性:
- 假设数组D存储每棵树的DDL
- 取出叶子节点中DDL最大的
- 如果他小于n,则不成立(第n个种谁都会晚于DDL)
- 如果他大于n,则什么时候种他都一样,不如最后种他,把机会留给其他人
- 然后删除该节点,递归处理
PS:中间计算要用int128(NOIP现在都可以用这个了?!)
以下是AC的代码:
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define ll long long
int n,dep[MAXN],fa[MAXN];
bool leaf[MAXN],vis[MAXN];
ll a[MAXN],b[MAXN],c[MAXN],d[MAXN];//planted before this day
vector<int>g[MAXN];
struct node{
ll nday;
ll npos;
};
priority_queue<node>q;
bool operator<(node x,node y){
return x.nday < y.nday;
}
__int128 sum(int pos,int l,int r){
if(c[pos] >= 0 || b[pos] + c[pos] * r > 0){
return ((__int128)(r - l + 1)) * b[pos] + ((__int128)(c[pos])) * (l + r) * (r - l + 1) / 2;
}
else if(b[pos] + c[pos] * l <= 0) return r - l + 1;
else {
ll tmp = b[pos] / (-c[pos]);
if(b[pos] % c[pos] == 0)tmp--;
return (__int128)(tmp - l + 1) * b[pos] + (__int128)c[pos] * (l + tmp) * (tmp - l + 1) / 2 + r - tmp;
}
}
void dfs(int pos,int depth) {
dep[pos] = depth;
int lens = g[pos].size();
if(lens == 1)leaf[pos] = true;
int t = 0;
for(int i = 0;i < lens;i++) {
if(dep[g[pos][i]])continue;
else {
t++;
fa[g[pos][i]] = pos;
dfs(g[pos][i],depth + 1);
}
}
if(t == 0)leaf[pos] = true;
return ;
}
int calc_day(int pos,int Max){
int lft1 = 1,rgt1 = Max;
//cout << a[pos] << ' ' << b[pos] << ' ' << c[pos] << ' ' << (ll)sum(pos,1,Max) << endl;
if(sum(pos,1,Max) < a[pos])return -1;
while(lft1 < rgt1){
int mid = (lft1 + rgt1 + 1) / 2;
if(sum(pos,mid,Max) >= a[pos])lft1 = mid;
else rgt1 = mid - 1;
}
return lft1;
}
bool check(int day) {
while(!q.empty())
q.pop();
for(int i = 1;i <= n;i++) {
vis[i] = false;
d[i] = calc_day(i,day);
//cout << d[i] << endl;
if(d[i] <= 0)return false;
if(leaf[i])q.push((node){d[i],i});
}
int tn = n;
while(tn >= 1){
node temp = q.top();
if(d[temp.npos] < tn)return false;
q.pop();
tn--;
vis[temp.npos] = true;
int lens = g[fa[temp.npos]].size(),f = 0;
for(int i = 0;i < lens;i++){
if(!vis[g[fa[temp.npos]][i]] && g[fa[temp.npos]][i] != fa[fa[temp.npos]]){
f = 1;break;
}
}
if(!f)q.push((node){d[fa[temp.npos]],fa[temp.npos]});
}
return true;
}
int main() {
//freopen("P9755_2.in","r",stdin);
scanf("%d",&n);
for(int i = 1;i <= n;i++) {
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
}
for(int i = 1;i < n;i++){
int ui,vi;
scanf("%d%d",&ui,&vi);
g[ui].push_back(vi);
g[vi].push_back(ui);
}
dfs(1,1);
//cout << check(1e9) << endl;
int lft = 1,rgt = 1e9;
while(lft < rgt){
int mid = (lft + rgt) / 2;
if(check(mid))rgt = mid;
else lft = mid + 1;
}
printf("%d\n",rgt);
return 0;
}