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

[CSP-S 2023] 种树

时间:2023-12-13 17:59:03浏览次数:22  
标签:return int mid times maxn 100010 2023 种树 CSP

[CSP-S 2023] 种树

Part - 1

特殊性质 B

将种树时间设为 \(l\),结束时间为 \(r\),则可以把数的高度记作:

\[\sum_{i = l}^r\max(1, b_i + x \times c_i) \]

分类讨论:

  1. \(c_i \ge 0\)

可以表示为 \(b_i \times (r - l + 1) + \frac{(r - l + 1) \times (r + l)}{2} \times c_i\)

  1. \(c_i < 0\)

先算出多少天之后 \(\max(1, b_i + x \times c_i) = 1\) 也就是 \(b_i + x \times c_i \le 1\),得到临界点为 \(x = \lfloor \frac{1 - b_i}{c} \rfloor\)。

  • \(x > r\)
    那么这时无时间段 \(\max(1, b_i + x \times c_i) = 1\),那么可以得到:

    \[\sum_{i = l}^r(b_i + x \times c_i) = b_i \times (r - l + 1) + \frac{(r - l + 1) \times (r + l)}{2} \times c_i \]

  • \(x < l\)
    此时从种树的第一天开始都只增加 \(1\),那么这时很好表示:

    \[r - l + 1 \]

  • \(l \le x \le r\)
    首先要算出没有到临界点之前的:

    \[b_i \times (maxn - l + 1) + \frac{(maxn - l + 1) \times (maxn + l)}{2} \times c_i \]

    再算出临界点之后的:

    \[r - maxn \]

    加起来得到

    \[b_i \times (maxn - l + 1) + \frac{(maxn - l + 1) \times (maxn + l)}{2} \times c_i + r - maxn \]

因为是链所以树种下去的时间一定是按照编号递增的,也就是第 \(i\) 天就会种下第 \(i\) 号树,所以我们可以取所有树 \(l = i\),完成时的 \(r\) 的值的 \(\max\)。

Code

#include <bits/stdc++.h>
#define rint register int
#define For(i, j, k) for(rint i = j; i <= k; ++i)
using namespace std;

inline int read(){
  int f = 1,x = 0;
  char ch = getchar();
  while(!isdigit(ch)){
	if(ch == '-')f = -1;
  	ch = getchar();
  }
  while(isdigit(ch)){
  	x = (x << 1) + (x << 3) + (ch ^ 48);
  	ch = getchar();
  }
  return x * f;
}
inline void print(int x){
  if(x > 9)print(x / 10);
  putchar(x % 10 + '0');
}

int n, b[100010], c[100010];
long long a[100010];

struct side{
	int to, next;
}s[200010];
int head[100010], tot;

inline void add(int from, int to){
	s[++tot] = side{to, head[from]};
	head[from] = tot;
}

inline bool check(int x, __int128 l, __int128 r){
	if(c[x] >= 0)return b[x] * (r - l + 1) + c[x] * (l + r) * (r - l + 1) / 2 >= a[x];
	__int128 maxn = (1 - b[x]) / c[x];
	if(maxn < l)return r - l + 1 >= a[x];
	else if(maxn > r)return b[x] * (r - l + 1) + c[x] * (l + r) * (r - l + 1) / 2 >= a[x];
	else return b[x] * (maxn - l + 1) + c[x] * (l + maxn) * (maxn - l + 1) / 2 + r - maxn >= a[x];
}	

signed main(){
	n = read();
	For(i, 1, n)
		scanf("%lld", a + i), b[i] = read(), c[i] = read();
	For(i, 1, n - 1){
		rint u = read(), v = read();
		add(u, v);
		add(v, u);
	}
	int ans = 1;
	For(i, 1, n){
		int l = 1, r = 1e9;
		while(l < r){
			int mid = l + (r - l) / 2;
			if(check(i, i, mid))r = mid;
			else l = mid + 1;
		}
		ans = max(ans, r);
	}
	cout << ans;
  return 0;
}

Part - 2

特殊性质 A

不难发现,此时 \(t_i = \lceil \frac{a_i}{b_i} \rceil\) ,此时有贪心策略,先将 \(t\) 大的满足,此时我们需要将 \(i\) 所有 \(i\) 到 \(1\) 的路径上的所有节点种上树,可以发现是一个从子节点到根节点的过程,同时需要倒序取出,可以使用栈,那么需要的时间就是种树的时间 \(x + t_i\) 的最大值。

Code

#include <bits/stdc++.h>
#define rint register int
#define For(i, j, k) for(rint i = j; i <= k; ++i)
using namespace std;


int n;

struct side{
	int to, next;
}s[200010];
int head[100010], tot;

inline void add(int from, int to){
	s[++tot] = side{to, head[from]};
	head[from] = tot;
}

int fa[100010];

inline void dfs(int u, int f){
	fa[u] = f;
	for(int i = head[u]; i; i = s[i].next){
		if(s[i].to != f)dfs(s[i].to, u);
	}
}

int p[100010], t[100010];
bool vis[100010];
int stacks[100010], top = 0;

signed main(){
	scanf("%d", &n);
	For(i, 1, n){
	    long long a;
	    int b, c;
		scanf("%lld%d%d", &a, &b, &c);
		t[i] = a / b;
		p[i] = i;
	}
	For(i, 1, n - 1){
	    int u, v;
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	cout << 1;
	dfs(1, 0);vis[0] = 1;
	sort(p + 1, p + 1 + n, [](int a, int b){return t[a] < t[b];});
	int ans = 0;
	for(int i = 1, x = 0; i <= n; i++){
		int now = p[i];
		while(!vis[now])vis[stacks[++top] = now] = 1, now = fa[now];
		while(tot)ans = max(ans, x++ + t[stacks[top--]]);
	}
	cout << ans;
	return 0;
}

有了以上两个特殊性质,正解就不难想了:

AC Code

#include <bits/stdc++.h>
#define rint register int
#define For(i, j, k) for(rint i = j; i <= k; ++i)
using namespace std;


int n, b[100010], c[100010];
long long a[100010];

struct side{
	int to, next;
}s[200010];
int head[100010], tot;

inline void add(int from, int to){
	s[++tot] = side{to, head[from]};
	head[from] = tot;
}

int fa[100010];

inline void dfs(int u, int f){
	fa[u] = f;
	for(int i = head[u]; i; i = s[i].next){
		if(s[i].to != f)dfs(s[i].to, u);
	}
}

inline __int128 check(int x, __int128 l, __int128 r){
	if(c[x] >= 0)return  (r - l + 1) * b[x] + (l + r) * (r - l + 1) / 2 * c[x];
	__int128 maxn = (1 - b[x]) / c[x];
	if(maxn < l)return r - l + 1;
	else if(maxn > r)return (r - l + 1) * b[x] + (l + r) * (r - l + 1) / 2 * c[x];
	else return (maxn - l + 1) * b[x] + (l + maxn) * (maxn - l + 1) / 2 * c[x] + r - maxn;
}	

int p[100010], t[100010]; 
bool vis[100010];

int stacks[100010];

inline bool solve(int s){
	for(int i = 1; i <= n; i++){
		if(check(i, 1, s) < a[i])return 0;
		int l = 1, r = n;
		while(l < r){
			int mid = (l + r + 1) >> 1;
			if(check(i, mid, s) >= a[i])l = mid;
			else r = mid - 1; 
		}
		p[i] = i, t[i] = l, vis[i] = 0;
	}
	sort(p + 1, p + 1 + n, [](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[stacks[++top] = now] = 1, now = fa[now];
		while(top) if(t[stacks[top--]] < ++x) return 0;
	}
	return 1;
}


signed main(){
	scanf("%d", &n);
	For(i, 1, n)
		scanf("%lld%d%d", a + i, b + i, c + i);
	For(i, 1, n - 1){
	    int u, v;
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	dfs(1, 0);vis[0] = 1;
	int l = n, r = 1e9;
	while(l < r){
		int mid = (l + r) >> 1;
		if(solve(mid))r = mid;
		else l = mid + 1;
	}
	cout << r;
	return 0;
}

标签:return,int,mid,times,maxn,100010,2023,种树,CSP
From: https://www.cnblogs.com/lightstarup/p/17899604.html

相关文章

  • [CSP-S 2023] 消消乐
    赛时想到了一个规律,当一个字符串的头和首相等,并且中间的字符串同样可以被消除的话,那么这个大字串也就可以被消除。虽然竭尽全力想到了这一点,不过还不知道如何实现,开始的想法是:先使用\(vector\)来记录每一个字母所在的分别的下标,然后先从两个相邻字母的开始找(因为这样必定是可......
  • 2023-12-13:用go语言,密码是一串长度为n的小写字母,一则关于密码的线索纸条, 首先将字母a
    2023-12-13:用go语言,密码是一串长度为n的小写字母,一则关于密码的线索纸条,首先将字母a到z编号为0到25编号,纸条上共有n个整数ai,其中a1表示密码里第一个字母的编号,若i>1的话就表示第i个字母和第i-1个字母编号的差值,例如,a2就代表密码中第1个字母和第2个字母编号的差值,若密码是acb,......
  • [CSP-S 2023] 密码锁
    [CSP-S2023]密码锁考场上我跟个\(somebody\)一样,一看就想:一眼乘法原理,乱搞写一下就出来了。当时我还算了一下暴力好像也不会超时,结果,每天在yz日以继日的颓废考试经验,我断定CSP-S是不会考这么\(!\)复杂的题目的,结果暴力出奇迹,就是枚举模拟。考试后,一看wc枚举,我断定我......
  • 2023.12.13日报
    最近事情比较多,写日报也有点怠惰了,主要是偶尔会忘记,简单总结一下这两天的工作首先是使用jfinal做大作业,实话说这玩意一开始我觉得并不好用,因为页面也很简陋,后端也有点看不懂但是在实际体验并且调用百度翻译和图像处理的api后,感觉用起来还可以,其实和springboot有点类似现在是已......
  • Solution Set 2023.12.13
    CF1736ESwapandTake设在第\(i\)次操作后产生贡献的值为初始序列的\(a_{p_i}\),可以发现产生的贡献的指针移动方式为每次向后移动\(1\),而通过交换数字最多可以使得某个数字每次向后移动\(1\),由此可以得出每次产生贡献的位置单调不减,即\(p_1\lep_2\le\cdots\lep_n\)......
  • 【2023-12-12】家庭分工
    20:00人们说得好,在耕种多年的田地里,一年又一年长出新的谷子;完全可以相信“从读过的旧书里”人们也一定能学到新的知识。                                                ......
  • 尊嘟假嘟?2023年人工智能行业新诞生10家独角兽,AIGC竟占近一半
    今年的AIGC持续热了一年,从王慧文等大佬的入局,到百度发布「文心一言」,各大巨头纷纷发布大模型产品,切实地给中国人工智能赛道的融资添了一把浓烈的火。回顾这即将过去的一整年,虽然2023年投融资整体行业遇冷,各种坏消息不断,但总体而言,AI行业融资的形势相对仍处于比较热门的状态。2......
  • 热烈祝贺2023才聚PMP发展论坛成功举行!
    12月10日,2023才聚PMP发展论坛在深圳成功举行!此次论坛由才聚主办,聚焦PMP发展前沿动态以及项目经理职业发展,吸引了近千名项目管理大咖齐聚一堂,共同展望PMP美好发展未来。大会现场座无虚席1大会团队精心筹备打造完美参会体验才聚对本次论坛高度重视,在筹备之初,就明确了要将论坛打造为项......
  • 2023十大优质国产原厂品牌网络评选活动!
    助力国产替代,振兴中国芯片!“道合顺2023十大优质国产原厂品牌网络评选”正式开启~投票有机会抽中国黄金金条、华为Mate60手机,小米扫地机器人等壕礼~!戳>https://www.icdhs.com/activity/brandvote2023/?source=51......
  • F. 纪念品 - 2023HBUCM程序设计竞赛/CSP-J2019
    题面小伟突然获得一种超能力,他知道未来\(T\)天\(N\)种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。每天,小伟可以进行以下两种交易无限次:任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;卖出持有的......