P9755 [CSP-S 2023] 种树 题解
首先考虑如何求出第 \(i\) 棵树在 \([l,r]\) 时间段能长多高。这个东西可以差分一下然后等差数列求和。放一下代码:
inline lll calc(int i,int x){
if(c[i]>=0){
return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
}else{
int d=(b[i]-1)/(-c[i]);
if(x<=d) return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
else return (lll)b[i]*d+(lll)c[i]*d*(d+1)/2+(x-d);
}
}
inline lll calc(int i,int l,int r){
return calc(i,r)-calc(i,l-1);
}
注:要开 __int128。
考虑二分完成时间 \(t\),则转化为判定能否在 \(t\) 时间之前完成任务。考虑对于每个点处理出 \(lim_i\) 表示最晚的合法栽种时间,再套一层二分解决。
接下来考虑贪心,按 \(lim\) 从小到大处理所有点。记录当前时间,若 \(1\sim x\) 的路径上还有其他点没有栽种(很明显是一段后缀),就先在这些点上种树,并更新时间。最后判断每个点的时间是否都满足条件即可。
时间复杂度 \(O(n\log^2 V)\),可以通过。实现起来可能稍微有点卡常,将不必要的 __int128、long long 换成 int 即可(只有 \(a\) 需要 long long)。
代码:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define gt getchar
#define pt putchar
#define fst first
#define scd second
#define SZ(s) ((int)s.size())
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef unsigned int uint;
const int N=1e5+5;
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
inline bool __(char ch){return ch>=48&&ch<=57;}
template<class T> inline void read(T &x){
x=0;bool sgn=0;char ch=gt();
while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
if(sgn) x=-x;
}
template<class T,class ...T1> inline void read(T &x,T1 &...x1){
read(x);
read(x1...);
}
template<class T> inline void print(T x){
static char stk[70];short top=0;
if(x<0) pt('-');
do{stk[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
while(top) pt(stk[top--]);
}
template<class T> inline void printsp(T x){
print(x);
putchar(' ');
}
template<class T> inline void println(T x){
print(x);
putchar('\n');
}
int n;
struct Edge{
int to,nxt;
}e[N<<1];
int head[N],cnt;
inline void add_edge(int f,int t){
e[++cnt].to=t;
e[cnt].nxt=head[f];
head[f]=cnt;
}
inline void add_double(int f,int t){
add_edge(f,t);
add_edge(t,f);
}
ll a[N];
int b[N],c[N];
inline lll calc(int i,int x){
if(c[i]>=0){
return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
}else{
int d=(b[i]-1)/(-c[i]);
if(x<=d) return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
else return (lll)b[i]*d+(lll)c[i]*d*(d+1)/2+(x-d);
}
}
inline lll calc(int i,int l,int r){
return calc(i,r)-calc(i,l-1);
}
int fa[N],p[N],lim[N];
bool vis[N];
void dfs(int u,int fa){
::fa[u]=fa;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fa) dfs(v,u);
}
}
inline bool check(int ti){
for(int i=1;i<=n;++i){
if(calc(i,ti)<a[i]) return 0;
int l=0,r=1e9;
auto chk=[&](int x){
return calc(i,x,ti)>=a[i];
};
while(l<r){
int mid=(l+r+1)>>1;
if(chk(mid)) l=mid;
else r=mid-1;
}
lim[i]=l,vis[i]=0,p[i]=i;
}
sort(p+1,p+n+1,[](int x,int y){
return lim[x]<lim[y];
});
int now=0;
for(int i=1;i<=n;++i){
int u=p[i];
if(vis[u]) continue;
int v=u;
while(!vis[v]&&v){
vis[v]=1;
now++;
v=fa[v];
}
if(now>lim[u]) return 0;
}
return 1;
}
signed main(){
read(n);
for(int i=1;i<=n;++i) read(a[i],b[i],c[i]);
for(int u,v,i=1;i<n;++i){
read(u,v);
add_double(u,v);
}
dfs(1,0);
int l=0,r=1e9;
while(l<r){
int mid=l+((r-l)>>1);
if(check(mid)) r=mid;
else l=mid+1;
}
println(l);
return 0;
}