题意:给一棵二叉树的中序遍历,问你能不能构造出一棵二叉树它的权值 ≤k\le k≤k
权值 val=max(∣au−av∣)(u∈subtree(v))val=max(|a_u-a_v|)(u\in subtree(v))val=max(∣au−av∣)(u∈subtree(v))
做法 1:
有一种策略是考虑当前处理的区间 [l,r][l,r][l,r]
选择一个合法的点 xxx 作为根,合法的定义为当前点的权值减去左边右边的 min,maxmin,maxmin,max 的绝对值 ≤k\le k≤k
然后递归处理 [l,x−1],[x+1,r][l,x-1],[x+1,r][l,x−1],[x+1,r] 如果合法那么当前区间一定合法
对一个区间暴力枚举根然后分治即可
复杂度 O(n2)O(n^2)O(n2)
注意到一个很 nicenicenice 的 tricktricktrick,就是两个指针分别从两个端点开始扫
扫到一个就递归处理
显然只需要用小的那边的长度就可以将一个大的分成一个小的和一个大的
像极了启发式合并
用一个小的的复杂度就可以将一个小的和一个大的合并成一个大的
复杂度 O(nlog(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] 满足条件的点 ppp 显然跨过 ppp 的区间都是满足条件的
然后就是上述做法了
做法 2:
考虑对每个点为根考虑它作为根能够满足条件的区间
求出最远的 li,ril_i,r_ili,ri,那么与它差的绝对值 ≤k\le k≤k,然后左端点在 [li,i][l_i,i][li,i],右端点在 [i,ri][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; }

探讨基于中序遍历构建权值受限二叉树的两种策略,涉及区间处理与分治算法,通过动态规划优化复杂度至O(nlog(n))。

被折叠的 条评论
为什么被折叠?



