CSP-S 模拟 19/11/09 小 X 的二叉树(启发式分裂)(扫描线)

题意:给一棵二叉树的中序遍历,问你能不能构造出一棵二叉树它的权值 ≤ k \le k k
权值 v a l = m a x ( ∣ a u − a v ∣ ) ( u ∈ s u b t r e e ( v ) ) val=max(|a_u-a_v|)(u\in subtree(v)) val=max(auav)(usubtree(v))


做法 1:
有一种策略是考虑当前处理的区间 [ l , r ] [l,r] [l,r]
选择一个合法的点 x x x 作为根,合法的定义为当前点的权值减去左边右边的 m i n , m a x min,max min,max 的绝对值 ≤ k \le k k
然后递归处理 [ l , x − 1 ] , [ x + 1 , r ] [l,x-1],[x+1,r] [l,x1],[x+1,r] 如果合法那么当前区间一定合法
对一个区间暴力枚举根然后分治即可
复杂度 O ( n 2 ) O(n^2) O(n2)
注意到一个很 n i c e nice nice t r i c k trick trick,就是两个指针分别从两个端点开始扫
扫到一个就递归处理
显然只需要用小的那边的长度就可以将一个大的分成一个小的和一个大的
像极了启发式合并
用一个小的的复杂度就可以将一个小的和一个大的合并成一个大的
复杂度 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))


upt:
刚刚才发现上面没有证明!完全考猜结论,下面来补一下
首先我们猜想,是不是需要所有区间都满足存在一个根使得它与区间中任意一个数的差 ≤ k \le k k
充分性:
若任意区间满足这个性质,那么可以构造出 Δ K \Delta _K ΔK 二叉树
我们可以从 [ 1 , n ] [1,n] [1,n] 开始,选择一个合法的,然后向两边递归
必要性:
一个二叉树的根显然与区间中的其它结点的绝对值之差 ≤ k \le k k,所以如果存在二叉树,那么每个区间都满足上述性质
所以问题可以转换为判断是不是每一个区间都满足条件
考虑分治,找到一个当前 [ l , r ] [l,r] [l,r] 满足条件的点 p p p 显然跨过 p p p 的区间都是满足条件的
然后就是上述做法了


做法 2:
考虑对每个点为根考虑它作为根能够满足条件的区间
求出最远的 l i , r i l_i,r_i li,ri,那么与它差的绝对值 ≤ k \le k k,然后左端点在 [ l i , i ] [l_i,i] [li,i],右端点在 [ i , r i ] [i,r_i] [i,ri] 的区间都满足条件,于是问题等价于给出一些矩阵,求一个大矩阵有没有被覆盖完
扫描线,维护区间最小值及最小值个数,常数有些大

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 2e5 + 5;
int T, n, k, mi[N][20], mx[N][20], lg[N];
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
int Mi(int l, int r){
	int x = lg[r - l + 1]; 
	return min(mi[l][x], mi[r - (1 << x) + 1][x]);
}
int Mx(int l, int r){
	int x = lg[r - l + 1];
	return max(mx[l][x], mx[r - (1 << x) + 1][x]);
}
bool ck(int ps, int l, int r){
	int MIN = min(Mi(l, ps - 1), Mi(ps + 1, r));
	int MAX = max(Mx(l, ps - 1), Mx(ps + 1, r));
	return mi[ps][0] - MIN <= k && MAX - mi[ps][0] <= k;
}
bool solve(int l, int r){
	if(l >= r) return true;
	int lp = l, rp = r;
	while(lp <= rp){
		if(ck(lp, l, r)){ return solve(l, lp - 1) && solve(lp + 1, r); }
		if(ck(rp, l, r)){ return solve(l, rp - 1) && solve(rp + 1, r); }
		++lp; --rp;
	} return false;
}
void Solve(){
	n = read(), k = read();
	for(int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
	for(int i = 1; i <= n; i++) mi[i][0] = mx[i][0] = read();
	for(int j = 1; (1 << j) <= n; j++)
		for(int i = 1; i + (1 << j) - 1 <= n; i++)
			mi[i][j] = min(mi[i][j - 1], mi[i + (1 << j - 1)][j - 1]),
			mx[i][j] = max(mx[i][j - 1], mx[i + (1 << j - 1)][j - 1]);
	if(solve(1, n)) puts("Yes");
	else puts("No");
}
int main(){ T = read(); while(T--) Solve(); return 0; }
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 2e5 + 5;
int T, n, k, mi[N][20], mx[N][20], lg[N];
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
int Mi(int l, int r){
	int x = lg[r - l + 1]; 
	return min(mi[l][x], mi[r - (1 << x) + 1][x]);
}
int Mx(int l, int r){
	int x = lg[r - l + 1];
	return max(mx[l][x], mx[r - (1 << x) + 1][x]);
}
int L[N], R[N];
int ul(int x){
	int l = 1, r = x; while(l < r){
		int mid = (l+r) >> 1; 
		if(Mx(mid, x) - mi[x][0] <= k && mi[x][0] - Mi(mid, x) <= k) r = mid;
		else l = mid + 1;
	} return l;
}
int ur(int x){
	int l = x, r = n; while(l < r){
		int mid = (l+r+1) >> 1;
		if(Mx(x, mid) - mi[x][0] <= k && mi[x][0] - Mi(x, mid) <= k) l = mid;
		else r = mid - 1;
	} return l;
}
typedef pair<int, int> pi;
vector<pi> v[N];
#define mp make_pair
namespace seg{
	int mi[N << 2], tg[N << 2], ct[N << 2];
	#define mid ((l+r)>>1)
	void pushup(int x){ 
		mi[x] = min(mi[x<<1], mi[x<<1|1]); ct[x] = 0;
		if(mi[x] == mi[x<<1]) ct[x] += ct[x<<1];
		if(mi[x] == mi[x<<1|1]) ct[x] += ct[x<<1|1]; 
	}
	void pushnow(int x, int v){ mi[x] += v; tg[x] += v; }
	void pushdown(int x){ if(tg[x]) pushnow(x<<1, tg[x]), pushnow(x<<1|1, tg[x]), tg[x] = 0; }
	void build(int x, int l, int r){ tg[x] = mi[x] = 0; ct[x] = 1; if(l == r) return; build(x<<1, l, mid); build(x<<1|1, mid+1, r); }
	void modify(int x, int l, int r, int L, int R, int v){
		if(L<=l && r<=R){ pushnow(x, v); return; } pushdown(x);
		if(L<=mid) modify(x<<1, l, mid, L, R, v); 
		if(R>mid) modify(x<<1|1, mid+1, r, L, R, v); pushup(x);
	}
}
bool check(){
	for(int i = 1; i <= n; i++){
		L[i] = ul(i); R[i] = ur(i);
		v[L[i]].push_back(mp(i, R[i]));
		if(i < n) v[i + 1].push_back(mp(-i, -R[i]));
	}
	for(int i = 1; i <= n; i++){
		for(int j = 0; j < v[i].size(); j++){
			pi nx = v[i][j]; int l = nx.first, r = nx.second;
			if(l < 0) seg::modify(1, 1, n, -l, -r, -1);
			else seg::modify(1, 1, n, l, r, 1);
		} if(seg::mi[1]) continue;
		int ct = seg::ct[1]; if(ct > i - 1) return false;
	} return true;
}
void Solve(){
	n = read(), k = read();
	seg::build(1, 1, n);
	for(int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
	for(int i = 1; i <= n; i++) mi[i][0] = mx[i][0] = read();
	for(int j = 1; (1 << j) <= n; j++)
		for(int i = 1; i + (1 << j) - 1 <= n; i++)
			mi[i][j] = min(mi[i][j - 1], mi[i + (1 << j - 1)][j - 1]),
			mx[i][j] = max(mx[i][j - 1], mx[i + (1 << j - 1)][j - 1]);
	if(check()) puts("Yes");
	else puts("No");
}
void Clear(){ for(int i = 1; i <= n; i++) v[i].clear(); }
int main(){ T = read(); while(T--) Solve(), Clear(); return 0; }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值