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;
}
posted @ 2024-02-28 15:03  Southern_Dynasty  阅读(24)  评论(0编辑  收藏  举报