← 回到所有文章

P9755 [CSP-S 2023] 种树 卡常题解

前言 赛时没过,赛后乱糊!这个做法本来过了洛谷数据的,但是 CCF 数据太强了,竟然只有 40pts??? 而且这个做法有点卡常,需要修改一下,还是能跑过。 正文 0x00 分析题目 第一眼看着这个题…

题解文章的简约插画封面

前言

赛时没过,赛后乱糊!这个做法本来过了洛谷数据的,但是 CCF 数据太强了,竟然只有 40pts???

而且这个做法有点卡常,需要修改一下,还是能跑过。

正文

0x00 分析题目

第一眼看着这个题肯定是想要贪心去搞,但是发现不同时间种下一棵树,由于题目中的计算方式,最后需要的长高的时间并不一样的,所以肯定不能普通地贪心。既然不好确定开始时间,那就考虑确定结束时间。观察到答案具有单调性,考虑二分答案,这样确定了一个生长的结束时间后,很容发现开始时间需要满足的条件很简单,小于等于某个时间点即可。考虑对于每个节点 uu 把这个东西算出来,记作 tut_u。现在就可以进行贪心了。

考虑从根节点开始向下扩散,选择点种树,具体来说维护一个以 tut_u 为关键字的小根堆,每次选择小根对顶部点 uu 进行种树,看当前时刻是否小于等于 tut_u,然后遍历其儿子 vv,把 vv 放入小根堆。这样就能保证种某一颗树时,其祖先全部种上了树。但是直接这样做并不对,因为一个点的 tut_u 可能看起来很大,但是其子树内可能有个点的 tvt_v 很小,所以需要把 tut_u 处理一下:

tu=min⁡v∈treeutvt_u=\min_{v\in tree_u} t_v

变成子树内最小的值,即可。

0x01 代码实现

记 Fs,t,uF_{s,t,u} 表示 uu 这个树在 [s,t]\left[s,t\right] 这段时间内生长高度,那么有:

Fs,t,u=∑i=stmin⁡(bi+ci×i,1)F_{s,t,u}=\sum_{i=s}^{t}\min\left(b_i+c_i\times i,1\right)

当 ∀i∈[s,t],bi+ci×i≥1\forall i\in\left[s,t\right],b_i+c_i\times i\geq 1 时:

Fs,t,u=(s+t)(t−s+1)cu2+(t−s+1)buF_{s,t,u}=\frac{\left(s+t\right)\left(t-s+1\right)c_u}{2}+\left(t-s+1\right)b_u

其他情况下,不妨算出 pup_u 使得 i=pi,bi+ci×i<1i=p_i,b_i+c_i\times i\lt 1,显然:

pu=⌊bu−1−cu⌋p_u=\lfloor\frac{b_u-1}{-c_u}\rfloor Fs,t,u=Fs,pu,u+t−pu+1F_{s,t,u}=F_{s,p_u,u}+t-p_u+1

对于求出 tut_u,不想推式子的话二分就好了,用 Fs,t,uF_{s,t,u} 去 check。

发现 Fs,t,uF_{s,t,u} 会超过 2642^{64},但是不会超过 21282^{128},可以选择用 int128 记录,但是这样会被卡常。然后可以发现长得太高没有用于是可以算出个 int128 然后和 1e181e18 取 min⁡\min 即可。

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;
}

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭