P9755 [CSP-S 2023] 种树 卡常题解
前言 赛时没过,赛后乱糊!这个做法本来过了洛谷数据的,但是 CCF 数据太强了,竟然只有 40pts??? 而且这个做法有点卡常,需要修改一下,还是能跑过。 正文 0x00 分析题目 第一眼看着这个题…
前言
赛时没过,赛后乱糊!这个做法本来过了洛谷数据的,但是 CCF 数据太强了,竟然只有 40pts???
而且这个做法有点卡常,需要修改一下,还是能跑过。
正文
0x00 分析题目
第一眼看着这个题肯定是想要贪心去搞,但是发现不同时间种下一棵树,由于题目中的计算方式,最后需要的长高的时间并不一样的,所以肯定不能普通地贪心。既然不好确定开始时间,那就考虑确定结束时间。观察到答案具有单调性,考虑二分答案,这样确定了一个生长的结束时间后,很容发现开始时间需要满足的条件很简单,小于等于某个时间点即可。考虑对于每个节点 把这个东西算出来,记作 。现在就可以进行贪心了。
考虑从根节点开始向下扩散,选择点种树,具体来说维护一个以 为关键字的小根堆,每次选择小根对顶部点 进行种树,看当前时刻是否小于等于 ,然后遍历其儿子 ,把 放入小根堆。这样就能保证种某一颗树时,其祖先全部种上了树。但是直接这样做并不对,因为一个点的 可能看起来很大,但是其子树内可能有个点的 很小,所以需要把 处理一下:
变成子树内最小的值,即可。
0x01 代码实现
记 表示 这个树在 这段时间内生长高度,那么有:
当 时:
其他情况下,不妨算出 使得 ,显然:
对于求出 ,不想推式子的话二分就好了,用 去 check。
发现 会超过 ,但是不会超过 ,可以选择用 int128 记录,但是这样会被卡常。然后可以发现长得太高没有用于是可以算出个 int128 然后和 取 即可。
AC CODE
#include<bits/stdc++.h>
// #define ONLINE_JUDGE
#define INPUT_DATA_TYPE long long
#define OUTPUT_DATA_TYPE long long
INPUT_DATA_TYPE read(){register INPUT_DATA_TYPE x=0;register char f=0,c=getchar();while(c<'0'||'9'<c)f=(c=='-'),c=getchar();while('0'<=c&&c<='9')x=(x<<3)+(x<<1)+(c&15),c=getchar();return f?-x:x;}void print(OUTPUT_DATA_TYPE x){if(x<0)x=-x,putchar('-');if(x>9)print(x/10);putchar(x%10^48);return;}
std::vector<int> e[100010];
void addEdge(int u,int v){
e[u].push_back(v);
return;
}
int n;
long long a[100010],b[100010],c[100010],tim[100010],p[100010];
std::bitset<100010> vis;
struct NODE{
long long w;
int u;
bool operator < (const NODE o) const{
return w>o.w;
}
};
std::priority_queue<NODE> Q;
long long abss(long long x){return x<0?-x:x;}
long long floor_(long long a,long long b);
long long ceil_(long long a,long long b){
if(a>=0&&b>=0) return (a+b-1)/b;
else if(a<0&&b<0) return ((-a)+(-b)-1)/(-b);
else return -floor_(abss(a),abss(b));
}
long long floor_(long long a,long long b){
if(a>=0&&b>=0) return a/b;
else if(a<0&&b<0) return (-a)/(-b);
else return -ceil_(abss(a),abss(b));
}
long long getH(long long start,long long end,int u){
if(start>end) return 0;
if(c[u]<0&&p[u]+1<=end) return std::min((__int128_t)std::max(end-std::max(p[u]+1,start)+1,0ll)+(__int128_t)getH(start,p[u],u),(__int128_t)0x3f3f3f3f3f3f3f3fll);
return std::min((__int128_t)(start+end)*(__int128_t)(end-start+1)/2*(__int128_t)c[u]+(__int128_t)b[u]*(__int128_t)(end-start+1),(__int128_t)0x3f3f3f3f3f3f3f3fll);
}
long long calc(long long end,int u){
register long long l=1,r=end,mid;
while(l<r){
mid=(l+r)>>1;
if(getH(mid,end,u)>=a[u]) l=mid+1;
else r=mid;
}
return l;
}
void getPri(int u,int p){
for(auto v:e[u])
if(v!=p)
getPri(v,u),tim[u]=std::min(tim[u],tim[v]);
return;
}
bool solve(long long ans){
register int i,u,v;
register long long now=1;
for(i=1;i<=n;++i) tim[i]=calc(ans,i),vis[i]=0;
for(i=1;i<=n;++i) if(getH(tim[i]-1,ans,i)<a[i]||tim[i]==1) return false;
getPri(1,1);
while(!Q.empty()) Q.pop();
Q.push((NODE){0,1});
while(!Q.empty()){
u=Q.top().u,Q.pop();
if(vis[u]) continue;
vis[u]=1;
if(now>=tim[u]) return false;
for(auto v:e[u])
if(!vis[v])
Q.push((NODE){tim[v],v});
++now;
}
return true;
}
int main(){
register int i,flg=0,u,v;
register long long l,r=1000000010ll,mid;
n=read();
for(i=1;i<=n;++i){
a[i]=read();
b[i]=read();
c[i]=read();
flg|=c[i];
if(c[i]<0) p[i]=((b[i]-1)/(-c[i]));
}
for(i=1;i<n;++i){
u=read();
v=read();
addEdge(u,v);
addEdge(v,u);
}
l=0;
while(l<r){
mid=(l+r)>>1;
if(solve(mid)) r=mid;
else l=mid+1;
}
print(l);
return 0;
}谢谢你,读到这里。
本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗