题意:
给一个拥有 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;
}