2022年浙大城市学院新生程序设计竞赛 G Red Black Tree

题目链接

题意:

给一个拥有 n n n 层的红黑树,第一层有一片叶子,第二层有两片叶子…第 n n n 层有 n n n 片叶子。第 i i i 行第 j j j 片叶子表示为 (i, j)(i,j) , 如下图,所以除了第 nn 层的叶子之外,每一片叶子下面都拥有两片叶子,即叶子 (i, j)(i,j) 下面的叶子是 (i+1,j)(i+1,j) 和 (i+1,j+1)(i+1,j+1) 。

之后给定 k k k 片叶子的位置 (x, y)(x,y),表示该叶子为黑色,其他叶子为红色。

现给定两条规则
若叶子为黑色,则它下面两片叶子也要为黑色。
若它下面两片叶子都是黑色,则它也要为黑色。

现在可以将部分红色叶子染黑,使得每一个黑色叶子都满足上面的规则,同时让染黑的叶子数量最少。

问最终树上黑色叶子数量是多少?

思路:

相信看到题面的两条规则的第一条规则,大家都会很快的想到一个叶子节点是黑色,那么他的子树肯定全部被染成黑色,难点在于第二个条件,思路我一直仅限于暴力遍历的阶段,有一个很容易去想的思路就是我们可以发现一个点如果在另一个子树的相邻位置,我们就可以构造成一个更大的子树,然后暴力一行一行的维护最大值,想了10min之后我发现没有办法去处理这个东西。然后我就分析两个黑点之间的关系,如果两个黑点不相交的话分开处理,如果相交的话,我们可以发现他可以构造一个更大的三角形(子树),然后底是两个相交的最左端,和两个相交的最右端组成的一个三角形,然后转化成一道线段合并问题,然后贡献就是线段为底的三角形的个数(1 + n) * n / 2 。

代码如下

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <bits/stdc++.h>

#define x first
#define y second 

#define int long long 

using namespace std ;

int read(){
	int res = 0 , flag = 1 ;
	char c = getchar() ;
	while(!isdigit(c)){
		if(c == '-') flag = -1 ;
		c = getchar() ;
	}
	while(isdigit(c)){
		res = (res << 1) + (res << 3) + (c ^ 48) ;
		c = getchar() ;
	}
	return res * flag ; 
}

const int N = 1e6 + 10 ;
typedef pair<int , int> pii ;
typedef pair<double ,double> pdd ;

pii line[N] ;

int C2(int x){
	// cout << x << endl ; 
	return x * (x + 1) / 2 ; 
}
void solve() {
	int n = read() , k = read() ;
	for(int i = 1 ; i <= k ; i ++){
		int x = read() , y = read() ;
		// cout << y << " " << y + n - x  << endl ; 
		line[i] = {y , y + n - x} ; 
	}
	sort(line + 1 , line + 1 + k) ; 
	int ll = 0 , rr = -1e9 ;
	int ans = 0 ;  
	for(int i = 1 ; i <= k ; i ++){
		if(rr < line[i].x){
			// cout << rr - ll + 1 << endl ;
			if(rr >= ll)
				ans +=  C2(rr - ll + 1) ;
			ll = line[i].x ;
			rr = line[i].y ;
		}
		else {
			rr = max(rr , line[i].y) ; 
		}
		// cout << ll << " " << rr << endl ; 
	}
	// cout << rr - ll + 1 << endl ;
	ans += C2(rr - ll + 1) ; 
	cout << ans << endl ; 
}

signed main(void){
	time_t begin,end;
    double ret;
    begin=clock() ;
    solve() ; 
    ret=double(end-begin)/CLOCKS_PER_SEC;
    // cout<<"runtime:   "<<ret<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值