P9755 [CSP-S 2023] 种树 题解
Demeanor_Roy · · 题解
-
原题链接
-
赛后重新想,不到十分钟就会了。感觉我是 zz。
本题解将通过链和特殊性质 A 两档部分分引导读者走向正解。请各位不要跳读。
链
我们首先来考虑链的部分分。
不难发现,这部分的关键是实现一个形如 check(x,l,r) 的函数,表示
不妨列出
先考虑
接下来考虑
至此这个函数实现完毕。而统计答案,你就对链上的第 check(i,i,r) 为真的
特殊性质 A
接着我们思考特殊性质 A。
不难发现此时每个结点生长到目标高度所需时间与种下的时间无关,即有
将结点按
实际实现过程中,我们标记一下每个结点是否种过树。当考虑当前结点
正解
想明白了前两个部分,正解就是容易的。
考虑一般情况与特殊性质 A 的区别,不难发现我们无法得到上述的
我们考虑二分答案,并修改
时间复杂度
代码:
#include<bits/stdc++.h>
using namespace std;
#define ___ __int128
const int N=1e5+10;
int n,b[N],c[N],p[N],t[N],fa[N],stk[N];
int h[N],e[N<<1],ne[N<<1],idx;
bool vis[N];
long long a[N];
inline void add(int a,int b)
{
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
inline void dfs(int u,int p){fa[u]=p;for(int i=h[u];~i;i=ne[i]) if(e[i]!=p) dfs(e[i],u);}
inline ___ calc(int x,___ l,___ r)
{
if(c[x]>=0) return (r-l+1)*b[x]+(r-l+1)*(l+r)/2*c[x];
___ T=(1-b[x])/c[x];
if(T<l) return r-l+1;
if(T>r) return (r-l+1)*b[x]+(r-l+1)*(l+r)/2*c[x];
return (T-l+1)*b[x]+(T-l+1)*(l+T)/2*c[x]+r-T;
}
inline bool check(int r)
{
for(int i=1;i<=n;i++)
{
if(calc(i,1,r)<a[i]) return false;
int dl=1,dr=n;
while(dl<dr)
{
int mid=(dl+dr+1)>>1;
if(calc(i,mid,r)>=a[i]) dl=mid;
else dr=mid-1;
}
p[i]=i;t[i]=dl;vis[i]=false;
}
sort(p+1,p+n+1,[](int A,int B){return t[A]<t[B];});
for(int i=1,x=0;i<=n;i++)
{
int now=p[i],top=0;
while(!vis[now]) vis[stk[++top]=now]=true,now=fa[now];
while(top) if(t[stk[top--]]<++x) return false;
}
return true;
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld%d%d",&a[i],&b[i],&c[i]);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(1,0);vis[0]=true;
int l=n,r=1e9;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d",l);
return 0;
}