首页 > 其他分享 >[题解]P9755 [CSP-S 2023] 种树

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

时间:2024-07-24 17:51:31浏览次数:16  
标签:__ P9755 int 题解 times late 种树 int128 CSP

P9755 [CSP-S 2023] 种树

迟来的补题


本题是让最小化所有树长到指定高度日期的最大值,于是想到二分答案。

那么,对于一个给定的期限\(x\),如何判断是否能在这个日期内完成任务呢?

首先我们发现前\(n\)天每天都要种树,那么假设我们已经知道了每个地块最晚哪个日期种树,能保证在期限\(x\)之内长到指定高度,我们就可以贪心地选择:把每个地块的最晚日期数组\(late\)从小到大排序,依次选择每一个元素:

  • 如果该元素已经被选择了,就什么操作也不做。
  • 如果该元素还没被选择,就先把祖先节点中没有被选择过的选择上(因为要在一个地块种树,必须先在它的祖先种树),每种一棵树\(tim+1\)。

每个元素\(i\)选完后,判断\(tim\)和\(late_i\)的大小关系,如果\(tim>late_i\),说明当前这棵树已经种晚了,返回false

为什么这样贪心地选日期要求最早(种树需求最紧迫)的,正确性就能得到保证呢?

我们考虑\(2\)个地块,第\(1\)个地块最晚种树日期\(<\)第\(2\)个地块,这说明第\(1\)个地块种树的需求更紧迫。按照贪心的思想,我们先选第\(1\)个,那么必须把它的祖先地块全都种上树,再考虑第\(2\)个。如果我们先选第\(2\)个地块种上树,那么选第\(1\)个地块时,仍然避免不了要先在它的祖先地块上种树。因此后者不一定能得到最优答案。

现在还有\(1\)个问题:如何求\(late\)数组?

这就跟输入的\(b,c\)有关系了。我们知道第\(j\)天第\(i\)棵树会生长\(\max\{b_i+c_i\times j,1\}\)米,因此我们要找的\(late_i\),就是(\(x\)表示日期限制):

\[\large{\max\limits_{\sum\limits_{j=k}^x \max\{b_i+c_i\times j,1\}\ge a_i}k} \]

我们可以\(O(1)\)求出\(\sum\limits_{j=k}^x b_i+c_i\times j\),因此可以二分求出\(k\)作为\(late_i\)。

具体怎么求呢?我们设\(\sum\limits_{j=l}^r b_x+c_x\times j\)为\(f(l,r,x)\)。

计算\(f(l,r,x)\),可以对\(c_x\)的值进行分类讨论。

如果\(c_x\ge 0\),那么我们完全不需要考虑和\(1\)取\(\max\),直接套用等差数列求和公式即可:

\[(r-l+1)\times b_x+(l+r)\times \frac{r-l+1}{2}\times c_x \]

如果\(c_x<0\),我们解一个不等式,得到第几项开始值\(<1\),再分成两部分分别计算,再求和。

\[b_x+c_x\times k<1\\ k>\frac{1-b_x}{c_x}\]

故两部分分别是:

  • \(j\in [l,k]:\quad b_x+c_x\times j\ge 1\)
    求和:\((k-l+1)\times b+\frac{(l+k)\times(k-l+1)}{2}\times c\)
  • \(j\in [k+1,r]:\quad b_x+c_x\times j<1\)
    求和:\(r-k\)
    注意这里只给出了\(k\in[l,r)\)的情况,代码实现需要注意特判只有一个部分的情况,可以结合代码理解。

时间复杂度:(二分)套(二分+排序),外层二分\(O(\log V)\)(\(V\)是值域\(10^9\)),内层\(O(n\log n)\),总共\(O(n\log V\log n)\)。

注意:

  • \(f()\)计算出的值可能超出long long,需要开__int128

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 100010
using namespace std;
int n,a[N],b[N],c[N],k[N],late[N],fa[N],pos[N];
bool vis[N];
vector<int> G[N];
__int128 calc(int l,int r,int x){//第x棵树在[l,r]天能长到多少
	if(c[x]>=0) return (__int128)(r-l+1)*b[x]+(__int128)(l+r)*(r-l+1)/2*c[x];
	if(k[x]<l) return r-l+1;//只有右边
	if(k[x]>=r) return (__int128)(r-l+1)*b[x]+(__int128)(l+r)*(r-l+1)/2*c[x];//只有左边
	return (__int128)(r-k[x])+(__int128)(k[x]-l+1)*b[x]+(__int128)(l+k[x])*(k[x]-l+1)/2*c[x];
}
void dfs(int u,int f){
	fa[u]=f;
	for(int i:G[u])
		if(i!=f) dfs(i,u);
}
bool cmp(int a,int b){return late[a]<late[b];}
int cnt;
bool check(int x){//x天能否完成
	for(int i=1;i<=n;i++){//二分找每个位置最晚种树时间
		int l=1,r=n;
		while(l<r){
			int mid=(l+r+1)>>1;
			if(calc(mid,x,i)>=a[i]) l=mid;
			else r=mid-1;
		}
		if(l>x) return 0;
		pos[i]=i,late[i]=l;
	}
	sort(pos+1,pos+1+n,cmp);
	int tim=0;
	memset(vis,0,sizeof vis);
	vis[0]=1;//不加下面会死循环
	for(int i=1;i<=n;i++){
		for(int j=pos[i];!vis[j];j=fa[j])
			vis[j]=1,tim++;
		if(tim>late[pos[i]]) return 0;
	}
	return 1;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i]>>c[i];
		if(c[i]<0) k[i]=(1-b[i])/c[i];
	}
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		G[u].emplace_back(v);
		G[v].emplace_back(u);
	}
	dfs(1,0);
	int l=n,r=1e9;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l<<"\n";
	return 0;
}

标签:__,P9755,int,题解,times,late,种树,int128,CSP
From: https://www.cnblogs.com/Sinktank/p/18321174

相关文章

  • codeforces div_2 961 题解报告
    codeforcesdiv_2961题解报告比赛链接:https://codeforces.com/contest/1995A.Diagonals题目翻译给定一个边长为\(n\)的正方形,给定\(k\),要往正方形选\(k\)个点,\(x+y\)相同的点构成对角线,问至少要几条对角线才能装下\(k\)个点。时限1s,空间限制256MB\(1\len\le100,0\l......
  • 题解:牛客多校第三场 A
    ABridgingtheGap2时间限制:C/C++1秒,其他语言2秒空间限制:C/C++1048576K,其他语言2097152KSpecialJudge,64bitIOFormat:%lld题目描述Agroupof\(n\)walkersarrivesatariverbankatnight.Theywanttocrosstheriverusingaboat,whichisinitiallyont......
  • [题解]P3187 [HNOI2007] 最小矩形覆盖
    P3187[HNOI2007]最小矩形覆盖调了半天居然是因为没判断浮点精度误差才\(\colorbox{IndianRed}{\texttt{\color{White}{WA}}}\)了\(3\)个点,其他都没有问题!警钟长鸣……首先有一个结论:凸多边形的最小外接矩形一定和它的一条边重合。这个结论,网上几乎找不到完整的证明,目前发现......
  • [POI2012] PRE-Prefixuffix 题解
    前言题目链接:洛谷。题意简述给出长为\(n\)的串\(\texttt{S}\)。求最大的\(l\)满足:\[2l\leqn\land\texttt{S}[1\ldotsl]\doteq\texttt{S}[n-l+1\ldotsn]\]其中\(\doteq\)表示循环相同。题目分析看到循环相同,套路化想到,两个字符串一定可以表示成\(\tex......
  • 题解:2024牛客多校第三场 B
    BCrashTestheader时间限制:C/C++2秒,其他语言4秒空间限制:C/C++1048576K,其他语言2097152K64bitIOFormat:%lld题目描述Afterfiveyears,themosthigh-profileeventinmotorracing,Formula1,returnstoChina.TheChineseGrandPrixwasrecentlyheldatthe......
  • 题解:P10450 [USACO03MAR] Best Cow Fences G
    题目链接O(n^3)做法直接暴力枚举长度、起点,再全部跑一边求平均数。附上我丑陋的代码和提交记录,这个代码可以得42分。#include<bits/stdc++.h>usingnamespacestd;constintNR=1e5+5;longlongn,l,a[NR],sum,ave;intmain(){ cin>>n>>l; for(inti......
  • [CEOI2011] Matching 题解
    前言题目链接:洛谷。在上一题之后,模拟赛又放了一道KMP重定义相等的问题,但是寄了,故再记之。题意简述现在给出\(1\simn\)的排列\(p\)和序列\(h_1,h_2,\cdots,h_m\)​​,请你求出哪些\(h\)的子串符合排列\(p\)。串\(a_i\)符合一个排列被定义为其从小到大排序后得......
  • ABC250H 题解
    题面我们先考虑如何让连续的不在房子中的时间尽量短:我们考虑两个有房子的点\(x,y\),如果\(x\rightsquigarrowu\xrightarrow{w}v\rightsquigarrowy\)这条路径上除了\(x,y\)不存在有房子的点,那么我们可以找到这样一条路径,一定不劣:令\(a,b\)分别为最靠近\(u,v\)的有房......
  • 『模拟赛』暑假集训CSP提高模拟6
    RankA.花间叔祖签到题,但没切。一眼出思路找最大公因数,过了大样例发现同余的情况也合法,然后开始优化,成功从68pts到了88pts。考虑正解,首先答案不是一就是二。若答案是一,即所有数可在模某数\(x\)意义下同余。不妨将每个数化成\(a_i=k\timesx+d\)的形式,则若存在一个......
  • CF547D Mike and Fish 题解
    Description给定\(n\)个整点。你要给每个点染成红色或蓝色。要求同一水平线或垂直线上两种颜色的数量最多相差\(1\)。\(n,x_i,y_i\le2\times10^5\)。Solution考虑给每条水平线和垂直线建一个点,对于每个整点就把它对应的两条线连一条边。题目就转化为了给每条边定......