P9755 [CSP-S 2023] 种树
P9755 [CSP-S 2023] 种树
[CSP-S 2023] 种树
题目描述
你是一个森林养护员,有一天,你接到了一个任务:在一片森林内的地块上种树,并养护至树木长到指定的高度。
森林的地图有
你的目标是:在每片地块上均种植一棵树木,并使得
你每天可以选择一个未种树且与某个已种树的地块直接邻接(即通过单条道路相连)的地块,种一棵高度为
对每个地块而言,从该地块被种下树的当天开始,该地块上的树每天都会生长一定的高度。由于气候和土壤条件不同,在第
你想知道:最少需要多少天能够完成你的任务?
输入格式
输入的第一行包含一个正整数
接下来
接下来
输出格式
输出一行仅包含一个正整数,表示完成任务所需的最少天数。
数据范围
对于所有测试数据有:
Solution:
说句闲话
也是怀揣着勇气打开了一年前把我干死的题,开始以为c,b都是正整数,max(1,cx+b)就是一个笑话,然后我在教室yy了五分钟就认为自己把这题秒了
直到我第二次上机房看到“
首先我们不难想到二分答案,然后考虑对于每一个答案计算一个t[i],表示在该答案下,第i棵树最晚要在第t[i]天开始种植才能使得在ans天时满足题目的要求
然后我们思考一下怎么求这个天数:
设h(b,c,k)表示在从第1天到第k天,b=b,c=c的一颗树能长多高
形式化的:h=
我们会发现,这个式子就是求一个形如
那么h函数显然满足前缀和的性质,也就是说,一棵树在[L,R]段时间内生长的高度就是h(R)-h(L-1);
然后我们会发现,对于这个分段一次函数,我们只需要求出他们的交点然后
到这里这题就做完一半了,接下来的问题就是:在一颗树上,每个时刻只能对一个相父亲点被染过色的节点染色,编号为i的点的最晚染色时间为t[i],求是否有可行方案
这部分竟然可以直接贪心:
我们思考一个问题,我们假设显然即将要对x节点染色,下一个染色的节点是y,且t[x]<t[y];
那么我们将染x节点的路径x->1和染y所需的路径y->1画出来我们就会发现:
只要执行完了这两次操作,不论x,y哪一个先染,到后面那个点染完时,这两次操作过后的图是一样的。也就是说,完成这两个任务的最后时间是一样的,然而t[x]<t[y]显然x要比y的时间更紧迫,所以外面应该让x先染,否则就不保证check()函数的最优性了
然后还要注意的是h函数以及所有与h函数有关的值域在
然后这题就欢乐的做完了
Code
#include<bits/stdc++.h> typedef __int128 ll; const int N=1e5+5; using namespace std; int n; long long a[N]; int b[N],c[N],q[N],t[N]; int fa[N],vis[N]; int e_cnt; int head[N]; struct Edge{ int to,nxt; }e[N<<1]; inline void add(int x,int y) { e[++e_cnt]=(Edge){y,head[x]}; head[x]=e_cnt; } inline void write(ll x) { return; vector<int> a; while(x) { a.push_back(x%10); x/=10; } for(int i=a.size()-1;~i;i--){cout<<a[i];} } inline ll sum(ll x) { return ((x+1)*x)/2; } const __int128 one=1; inline ll h(ll b,ll c,ll R) { ll L=max(one,min(R+1,(ll)ceil(1.0*(1-b)/c))); ll res=0; if(c<0){return sum(L-1)*c+(L-1)*b+(R-L+1);} if(c==0){return b*R;} if(c>0){return (sum(R)-sum(L-1))*c+(R-L+1)*b+(L-1);} } inline void dfs(int x,int ff) { fa[x]=ff; for(int i=head[x],to;i;i=e[i].nxt) { to=e[i].to; if(to==fa[x])continue; dfs(to,x); } } inline int get_t(int x,ll val) { int l=1,r=n,ans=-1; while(l<=r) { int mid=l+r>>1; if(val-h(b[x],c[x],mid-1)>=a[x]) { l=mid+1; ans=mid; } else { r=mid-1; } } return ans; } inline void init() { for(int i=1;i<=n;i++){vis[i]=0;} vis[0]=1; } inline bool cmp(int x,int y) { return t[x]<t[y]; } inline void update(int x,int &res) { if(vis[x])return; vis[x]=1; res++; update(fa[x],res); } inline bool check(int k) { for(int i=1;i<=n;i++) { q[i]=i; ll val=h(b[i],c[i],k); if(val<a[i])return 0; t[i]=get_t(i,val); } sort(q+1,q+1+n,cmp); int qwq=0; for(int i=1;i<=n;i++) { update(q[i],qwq); if(qwq>t[q[i]])return 0; } return 1; } inline void work() { cin>>n; for(int i=1;i<=n;i++) { scanf("%lld%d%d",&a[i],&b[i],&c[i]); } for(int i=1,x,y;i<n;i++) { scanf("%lld%lld",&x,&y); add(x,y);add(y,x); } dfs(1,0); long long l=n,r=1e9,ans=1e9; while(l<=r) { int mid=l+r>>1; init(); if(check(mid)) { r=mid-1; ans=mid; } else { l=mid+1; } } printf("%lld\n",ans); } #undef int int main() { //freopen("P9755_2.in","r",stdin);freopen("P9755.out","w",stdout); work(); return 0; }