题目大意:
多次操作
区间取
m
i
n
min
min
区间求第
k
k
k 小
解题思路:
分块处理,设块大小为
p
p
p
块内排好序,查询时更新
二分答案,对整块直接
u
p
p
e
r
_
b
o
u
n
d
upper\_bound
upper_bound
对两边零散点暴力查
更新时间复杂度:
p
×
l
o
g
p
+
n
p
p × logp + \frac{n}{p}
p×logp+pn
查询时间复杂度:
l
o
g
n
×
(
n
p
×
l
o
g
p
+
p
)
logn × (\frac{n}{p} × logp + p)
logn×(pn×logp+p)
int n, m, a[maxn], size, bnum, lz[maxn];
int L[maxn], R[maxn]; vector <int> v[maxn];
inline int get_id(int x){
return x / size;
}
inline void update(int l, int r, int x){
int id = get_id(l);
if(L[id] < l){
v[id].clear();
for(int i=L[id]; i<=R[id]; i++){
a[i] = min(a[i], lz[id]);
if(i >= l && i <= r) a[i] = min(a[i], x);
v[id].push_back(a[i]);
}
sort(v[id].begin(), v[id].end());
id++;
}
if(id >= bnum) return;
while(R[id]<=r && id<bnum){
lz[id] = min(lz[id], x);
id++;
}
if(L[id]>r || id>=bnum) return;
v[id].clear();
for(int i=L[id]; i<=R[id]; i++){
a[i] = min(a[i], lz[id]);
if(i >= l && i <= r) a[i] = min(a[i], x);
v[id].push_back(a[i]);
}
sort(v[id].begin(), v[id].end());
}
inline int ck(int l, int r, int k){
int id = get_id(l), res = 0;
if(L[id] < l){
for(int i=l; i<=min(R[id], r); i++){
a[i] = min(a[i], lz[id]);
if(a[i] <= k) res++;
}
id++;
}
if(id >= bnum) return res;
while(R[id]<=r && id<bnum){
if(lz[id] <= k) res += R[id] - L[id] + 1;
else res += upper_bound(v[id].begin(), v[id].end(), k)\
- v[id].begin();
id++;
}
if(L[id]>r || id>=bnum) return res;
for(int i=L[id]; i<=min(R[id], r); i++){
a[i] = min(a[i], lz[id]);
if(a[i] <= k) res++;
}
return res;
}
inline int query(int L, int R, int k){
int l = 1, r = 1e9, mid;
while(l <= r){
mid = l + r >> 1;
if(ck(L, R, mid) >= k) r = mid - 1;
else l = mid + 1;
}
return l;
}
int main() {
scanf("%d%d", &n, &m);
for(int i=0; i<n; i++) scanf("%d", a+i);
size = sqrt(n), bnum = (n - 1) / size + 1;
for(int i=0; i<bnum; i++){
lz[i] = 0x3f3f3f3f;
L[i] = i * size, R[i] = i * size + size - 1;
if(i == bnum - 1) R[i] = n - 1;
for(int j=L[i]; j<=R[i]; j++) v[i].push_back(a[j]);
sort(v[i].begin(), v[i].end());
}
while(m--){
int op, l, r, k;
scanf("%d%d%d%d", &op, &l, &r, &k);
l--, r--;
if(op == 1) update(l, r, k);
else printf("%d\n", query(l, r, k));
}
}
即有撤销操作的时间分治
多次询问,每次询问可以有一种操作,可以撤回这种操作
若操作容易维护,但撤回操作不好弄,就可以离线下来
将询问看做线段树的叶子节点,一次操作就是只在一段时间内有效
因此就可以将这些操作按时间轴来区间覆盖,维护信息
然后在线段树上
d
f
s
dfs
dfs,进入节点时进行操作,离开时栈序撤销,到叶子就查询
int n, m, q, cnt, cnta, cntb;
int f[maxn], sz[maxn], ans[maxn];
map <int, int> mp[maxn];
int getf(int x) {
return x == f[x] ? x : getf(f[x]);
}
struct edge {
int u, v;
} ;
struct Rec {
int u, v, int szu, szv;
int cnt, cnta, cntb;
};
struct Tree {
vector <edge> e;
vector <Rec> rec;
} t[maxn<<2];
void build(int l, int r, int rt) {
t[rt].e.clear(), t[rt].rec.clear();
if(l == r) return;
int mid = l + r >> 1;
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
}
void update(int L, int R, edge ed, int l, int r, int rt) {
if(l>R || r<L) return;
if(l>=L && r<=R) {
t[rt].e.push_back(ed);
return;
}
int mid = l + r >> 1;
update(L, R, ed, l, mid, rt<<1);
update(L, R, ed, mid+1, r, rt<<1|1);
}
void dfs(int l, int r, int rt) {
int len = 0; Rec tmp;
for(auto i : t[rt].e) {
int u = i.u, v = i.v;
int fu = getf(u), fv = getf(v);
if(fu == fv) continue;
tmp.cnt = cnt, tmp.cnta = cnta, tmp.cntb = cntb;
cnt--;
if(sz[fu] == 1) cnta--;
if(sz[fv] == 1) cntb--;
if(sz[fu] < sz[fv]) swap(fu, fv);
tmp.u = fu, tmp.v = fv;
tmp.szu = sz[fu], tmp.szv = sz[fv];
f[fv] = fu, sz[fu] += sz[fv];
t[rt].rec.push_back(tmp); len++;
}
if(l == r) {
if(cntb != 0) ans[l] = -1;
else ans[l] = cnt - cnta;
} else {
int mid = l + r >> 1;
dfs(l, mid, rt<<1);
dfs(mid+1, r, rt<<1|1);
}
for(int i=len-1; ~i; i--) {
tmp = t[rt].rec[i];
f[tmp.u] = tmp.u, f[tmp.v] = tmp.v;
sz[tmp.u] = tmp.szu, sz[tmp.v] = tmp.szv;
cnt = tmp.cnt, cnta = tmp.cnta, cntb = tmp.cntb;
}
}
signed main() {
scanf("%d%d%d", &n, &m, &q);
build(1, q+1, 1);
for(int i=1; i<=n+m; i++) f[i] = i, sz[i] = 1;
cnt = n + m, cnta = n, cntb = m;
for(int i=1, k, x; i<=n; i++) {
scanf("%d", &k);
while(k--) {
scanf("%d", &x);
mp[i][x] = 1;
}
}
for(int i=2, u, v; i<=q+1; i++) {
scanf("%d%d", &v, &u);
if(mp[u][v] == 0) mp[u][v] = i;
else {
update(mp[u][v], i-1, {u, v+n}, 1, q+1, 1);
mp[u][v] = 0;
}
}
for(int i=1; i<=n; i++)
for(auto j : mp[i]) {
if(j.second == 0) continue;
int u = i, v = j.first;
update(j.second, q+1, {u, v+n}, 1, q+1, 1);
}
dfs(1, q+1, 1);
for(int i=2; i<=q+1; i++) printf("%d\n", ans[i]);
}
思想:对每个时刻的线段树维护每个元素最后出现的位置
int n, q, tot;
int t[maxn*30], root[maxn];
int ls[maxn*30], rs[maxn*30];
map <int, int> mp;
void update(int &rt, int pre, int pos, int c, int l, int r){
if(l>pos || r<pos) return ;
t[rt = ++tot] = t[pre];
ls[rt] = ls[pre], rs[rt] = rs[pre];
if(l == r){
t[rt] += c;
return;
}
int m = l + r >> 1;
update(ls[rt], ls[pre], pos, c, l, m);
update(rs[rt], rs[pre], pos, c, m+1, r);
t[rt] = t[ls[rt]] + t[rs[rt]];
}
int query(int rt, int pre, int l, int r){
if(l >= pre) return t[rt];
int m = l + r >> 1;
if(m >= pre) return query(ls[rt], pre, l, m) + t[rs[rt]];
else return query(rs[rt], pre, m+1, r);
}
int main() {
scanf("%d", &n);
for(int i=1, tmp, k; i<=n; i++){
scanf("%d", &tmp);
if(mp.count(tmp)){
k = 0;
update(k, root[i-1], mp[tmp], -1, 1, n);
update(root[i], k, i, 1, 1, n);
}
else update(root[i], root[i-1], i, 1, 1, n);
mp[tmp] = i;
}
scanf("%d", &q);
while(q--){
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(root[r], l, 1, n));
}
}
题目大意:
F
(
l
,
r
)
=
A
l
&
A
l
+
1
&
.
.
.
&
A
r
F(l, r) = A_l \& A_{l+1} \& ... \& A_{r}
F(l,r)=Al&Al+1&...&Ar
S
(
l
,
r
)
=
{
F
(
a
,
b
)
∣
m
i
n
(
l
,
r
)
≤
a
≤
b
≤
m
a
x
(
l
,
r
)
}
S(l, r) = \{F(a, b) | min(l, r) ≤ a ≤ b ≤ max(l, r)\}
S(l,r)={F(a,b)∣min(l,r)≤a≤b≤max(l,r)}
Q
Q
Q 次查询
l
l
l 和
r
r
r,求
S
(
l
,
r
)
S(l, r)
S(l,r)
解题思路:
一个数字连续作与操作,不同数字最多出现
l
o
g
log
log 个
因此对每个
a
i
a_i
ai,记录它往左不停作与操作出现的不同数字
这里我们要查询
S
(
l
,
r
)
S(l, r)
S(l,r),也就是以
r
r
r 为右端点,做与操作的不同数字且产生过程都在
l
l
l 右端
联想到主席树求区间不同数字的操作
这里就可以在记录
a
i
a_i
ai 往左不停作与操作出现的不同数字 的同时
维护产生这些数字的左端点的最大值
这个最大值
m
a
x
max
max 就是我们查询
S
(
l
,
r
)
S(l, r)
S(l,r) 时,
l
l
l 必须要
≤
m
a
x
≤max
≤max
因为所有的不同数字最多
n
l
o
g
n
nlogn
nlogn 个,因此直接用
m
a
p
map
map 维护最大值即可
时间复杂度:
O
(
n
l
o
g
2
n
+
q
l
o
g
n
)
O(nlog^2n +qlogn)
O(nlog2n+qlogn)
int T, n, q, lans, tot, root[maxn];
int t[maxn*300], ls[maxn*300], rs[maxn*300];
map <int, int> mp, tmp, las;
void update(int &rt, int pre, int pos, int c, int l, int r) {
if(l>pos || r<pos) return;
rt = ++tot;
t[rt] = t[pre] + c;
ls[rt] = ls[pre], rs[rt] = rs[pre];
if(l == r) return;
int mid = l + r >> 1;
update(ls[rt], ls[pre], pos, c, l, mid);
update(rs[rt], rs[pre], pos, c, mid+1, r);
}
int query(int rt, int L, int R, int l, int r) {
if(l>R || r<L) return 0;
if(l>=L && r<=R) return t[rt];
int mid = l + r >> 1, ret = 0;
ret += query(ls[rt], L, R, l, mid);
ret += query(rs[rt], L, R, mid+1, r);
return ret;
}
signed main() {
scanf("%d", &n);
for(int i=1, x; i<=n; ++i) {
scanf("%d", &x);
root[i] = root[i-1];
mp[x] = i; tmp.clear();
for(auto j : mp) {
int fi = j.first, se = j.second;
tmp[fi & x] = max(tmp[fi & x], se);
}
for(auto j : tmp) {
int fi = j.first, se = j.second;
if(las[fi] == se) continue;
update(root[i], root[i], las[fi], -1, 1, n);
las[fi] = se;
update(root[i], root[i], las[fi], 1, 1, n);
}
mp.swap(tmp);
}
scanf("%d", &q);
while(q--) {
int l, r;
scanf("%d%d", &l, &r);
l = (l ^ lans) % n + 1;
r = (r ^ lans) % n + 1;
if(l > r) swap(l, r);
printf("%d\n", lans = query(root[r], l, r, 1, n));
}
}
vector <int> v;
int n, m, tot, cnt, size, a[maxn];
int t[maxn*400], ls[maxn*400], rs[maxn*400];
int cnt1, cnt2, T[maxn], lt[maxn], rt[maxn];
struct node{
int op, l, r, k;
} q[maxn];
void update(int &rt, int pos, int c, int l, int r){
if(l>pos || r<pos) return;
if(!rt) rt = ++tot; // 继承上一棵树
t[rt] += c;
if(l == r) return;
int m = l + r >> 1;
update(ls[rt], pos, c, l, m);
update(rs[rt], pos, c, m+1, r);
}
void add(int x, int v){
for(int i=x; i<=n; i+=lowbit(i))
update(T[i], a[x], v, 1, size);
}
int query(int l, int r, int k){
if(l == r) return l;
int x = 0, m = l + r >> 1;
for(int i=1; i<=cnt1; i++) x -= t[ls[lt[i]]];
for(int i=1; i<=cnt2; i++) x += t[ls[rt[i]]];
if(x >= k){
for(int i=1; i<=cnt1; i++) lt[i] = ls[lt[i]];
for(int i=1; i<=cnt2; i++) rt[i] = ls[rt[i]];
return query(l, m, k);
} else {
for(int i=1; i<=cnt1; i++) lt[i] = rs[lt[i]];
for(int i=1; i<=cnt2; i++) rt[i] = rs[rt[i]];
return query(m+1, r, k-x);
}
}
inline int getid(int x){
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++){
scanf("%d", a+i);
v.push_back(a[i]);
}
for(int i=1; i<=m; i++){
char s[2]; scanf("%s", s);
if(s[0]=='Q'){
q[i].op = 1;
scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].k);
} else {
q[i].op = 2;
scanf("%d%d", &q[i].l, &q[i].r);
v.push_back(q[i].r);
}
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
size = v.size();
for(int i=1; i<=n; i++)
a[i] = getid(a[i]), add(i, 1);
for(int i=1; i<=m; i++){
if(q[i].op == 1){
cnt1 = cnt2 = 0;
for(int j=q[i].l-1; j; j-=lowbit(j)) lt[++cnt1] = T[j];
for(int j=q[i].r; j; j-=lowbit(j)) rt[++cnt2] = T[j];
printf("%d\n", v[query(1, size, q[i].k) - 1]);
} else {
add(q[i].l, -1);
a[q[i].l] = getid(q[i].r);
add(q[i].l, 1);
}
}
}
维护一个矩阵,实现以下几个需求:
①
:
①:
①:单点修改,区间查最值
②
:
②:
②:区间加,区间查和
③
:
③:
③:区间修改(递增),区间查最值
二维线段树本质上是树套树
因此无法实现
p
u
s
h
d
o
w
n
pushdown
pushdown,用标记永久化替代
暂时无法满足任意模式的区间修改,也可能是我太菜了
注意修改后的
m
e
r
g
e
merge
merge,只针对所修改区间进行
m
e
r
g
e
merge
merge 即可
建树复杂度:
O
(
n
2
)
O(n^2)
O(n2)
单次修改查询复杂度:
O
(
l
o
g
2
n
)
O(log^2n)
O(log2n)
建树后,单点修改,区间查最大最小值
int n, q, a[maxn][maxn];
struct seg{
ll t1[maxn<<2], t2[maxn<<2], lz[maxn<<2];
void build(int L, int l, int r, int rt){
if(l == r) {
t1[rt] = t2[rt] = a[L][r];
return;
}
int m = l + r >> 1;
build(L, l, m, rt<<1);
build(L, m+1, r, rt<<1|1);
t1[rt] = max(t1[rt<<1], t1[rt<<1|1]);
t2[rt] = min(t2[rt<<1], t2[rt<<1|1]);
}
void update(int L, int R, ll x, int l, int r, int rt){
t1[rt] = t2[rt] = x;
if(l==L && r==R){
lz[rt] = x; return;
}
int m = l + r >> 1;
if(m >= R) update(L, R, x, l, m, rt<<1);
else if(L > m) update(L, R, x, m+1, r, rt<<1|1);
else update(L, m, x, l, m, rt<<1), \
update(m+1, R, x, m+1, r, rt<<1|1);
t1[rt] = max(t1[rt<<1], t1[rt<<1|1]);
t2[rt] = min(t2[rt<<1], t2[rt<<1|1]);
}
ll query1(int L, int R, int l, int r, int rt, ll sum){
if(l==L && r==R) return t1[rt];
int m = l + r >> 1;
if(m >= R) return query1(L, R, l, m, rt<<1, sum);
else if(L > m) return query1(L, R, m+1, r, rt<<1|1, sum);
else return max( query1(L, m, l, m, rt<<1, sum), \
query1(m+1, R, m+1, r, rt<<1|1, sum) );
}
ll query2(int L, int R, int l, int r, int rt, ll sum){
if(l==L && r==R) return t2[rt];
int m = l + r >> 1;
if(m >= R) return query2(L, R, l, m, rt<<1, sum);
else if(L > m) return query2(L, R, m+1, r, rt<<1|1, sum);
else return min( query2(L, m, l, m, rt<<1, sum), \
query2(m+1, R, m+1, r, rt<<1|1, sum) );
}
} t[maxn<<2], lz[maxn<<2];
void merge(seg &p, seg &p1, seg &p2, int L, int R, int l, int r, int rt){
if(l>R || r<L) return;
p.t1[rt] = max(p1.t1[rt], p2.t1[rt]);
p.t2[rt] = min(p1.t2[rt], p2.t2[rt]);
if(l == r) return;
int m = l + r >> 1;
merge(p, p1, p2, L, R, l, m, rt<<1);
merge(p, p1, p2, L, R, m+1, r, rt<<1|1);
}
void build(int l, int r, int rt){
if(l == r){
t[rt].build(l, 1, n, 1);
return;
}
int m = l + r >> 1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
merge(t[rt], t[rt<<1], t[rt<<1|1], 1, n, 1, n, 1);
}
void update(int x, int y, int xx, int yy, ll v, int l, int r, int rt){
t[rt].update(y, yy, v, 1, n, 1);
if(l==x && r==xx){
lz[rt].update(y, yy, v, 1, n, 1);
return;
}
int m = l + r >> 1;
if(m >= xx) update(x, y, xx, yy, v, l, m, rt<<1);
else if(x > m) update(x, y, xx, yy, v, m+1, r, rt<<1|1);
else update(x, y, m, yy, v, l, m, rt<<1), \
update(m+1, y, xx, yy, v, m+1, r, rt<<1|1);
merge(t[rt], t[rt<<1], t[rt<<1|1], y, yy, 1, n, 1);
}
ll query1(int x, int y, int xx, int yy, int l, int r, int rt, ll sum){
if(l==x && r==xx) return t[rt].query1(y, yy, 1, n, 1, 0);
int m = l + r >> 1;
if(m >= xx) return query1(x, y, xx, yy, l, m, rt<<1, sum);
else if(x > m) return query1(x, y, xx, yy, m+1, r, rt<<1|1, sum);
else return max( query1(x, y, m, yy, l, m, rt<<1, sum), \
query1(m+1, y, xx, yy, m+1, r, rt<<1|1, sum) );
}
ll query2(int x, int y, int xx, int yy, int l, int r, int rt, ll sum){
if(l==x && r==xx) return t[rt].query2(y, yy, 1, n, 1, 0);
int m = l + r >> 1;
if(m >= xx) return query2(x, y, xx, yy, l, m, rt<<1, sum);
else if(x > m) return query2(x, y, xx, yy, m+1, r, rt<<1|1, sum);
else return min( query2(x, y, m, yy, l, m, rt<<1, sum), \
query2(m+1, y, xx, yy, m+1, r, rt<<1|1, sum) );
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%lld", &a[i][j]);
build(1, n, 1);
scanf("%d", &q);
while(q--){
char op; int x, y, xx, yy, w;
scanf(" %c", &op);
if(op == 'c') {
scanf("%d %d %d", &x, &y, &w);
update(x, y, x, y, w, 1, n, 1);
} else {
scanf("%d %d %d %d", &x, &y, &xx, &yy);
printf("%lld ", query1(x, y, xx, yy, 1, n, 1, 0));
printf("%lld\n", query2(x, y, xx, yy, 1, n, 1, 0));
}
}
}
查询区间最大值,用最大值
+
+
+
w
w
w 更新区间
注意这里因为标记可以不断取
m
a
x
max
max,才可以实现区间更新
int d, s, q;
struct seg{
int t[maxn<<2], lz[maxn<<2];
void build(int L, int l, int r, int rt){
if(l == r) {
// t[rt] = a[L][r];
return;
}
int m = l + r >> 1;
build(L, l, m, rt<<1);
build(L, m+1, r, rt<<1|1);
t[rt] = max(t[rt<<1], t[rt<<1|1]);
}
void update(int L, int R, int x, int l, int r, int rt){
t[rt] = max(t[rt], x);
if(l==L && r==R){
lz[rt] = max(lz[rt], x);
return;
}
int m = l + r >> 1;
if(m >= R) update(L, R, x, l, m, rt<<1);
else if(L > m) update(L, R, x, m+1, r, rt<<1|1);
else update(L, m, x, l, m, rt<<1), \
update(m+1, R, x, m+1, r, rt<<1|1);
// t[rt] = max(t[rt<<1], t[rt<<1|1]);
}
int query(int L, int R, int l, int r, int rt){
if(l==L && r==R) return t[rt];
int m = l + r >> 1, ret = lz[rt];
if(m >= R) ret = max(ret, query(L, R, l, m, rt<<1));
else if(L > m) ret = max(ret, query(L, R, m+1, r, rt<<1|1));
else ret = max(ret, max( query(L, m, l, m, rt<<1), \
query(m+1, R, m+1, r, rt<<1|1) ) );
return ret;
}
} t[maxn<<2], lz[maxn<<2];
void merge(seg &p, seg &p1, seg &p2, int l, int r, int rt){
p.t[rt] = max(p1.t[rt], p2.t[rt]);
if(l == r) return;
int m = l + r >> 1;
merge(p, p1, p2, l, m, rt<<1);
merge(p, p1, p2, m+1, r, rt<<1|1);
}
void build(int l, int r, int rt){
if(l == r){
t[rt].build(l, 1, s, 1);
return;
}
int m = l + r >> 1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
merge(t[rt], t[rt<<1], t[rt<<1|1], 1, s, 1);
}
void update(int x, int y, int xx, int yy, int v, int l, int r, int rt){
t[rt].update(y, yy, v, 1, s, 1);
if(l==x && r==xx){
lz[rt].update(y, yy, v, 1, s, 1);
return;
}
int m = l + r >> 1;
if(m >= xx) update(x, y, xx, yy, v, l, m, rt<<1);
else if(x > m) update(x, y, xx, yy, v, m+1, r, rt<<1|1);
else update(x, y, m, yy, v, l, m, rt<<1), \
update(m+1, y, xx, yy, v, m+1, r, rt<<1|1);
// merge(t[rt], t[rt<<1], t[rt<<1|1], 1, s, 1);
}
int query(int x, int y, int xx, int yy, int l, int r, int rt){
if(l==x && r==xx) return t[rt].query(y, yy, 1, s, 1);
int m = l + r >> 1, ret = lz[rt].query(y, yy, 1, s, 1);
if(m >= xx) ret = max(ret, query(x, y, xx, yy, l, m, rt<<1));
else if(x > m) ret = max(ret, query(x, y, xx, yy, m+1, r, rt<<1|1));
else ret = max(ret, max( query(x, y, m, yy, l, m, rt<<1), \
query(m+1, y, xx, yy, m+1, r, rt<<1|1) ) );
return ret;
}
int main() {
scanf("%d%d%d", &d, &s, &q);
while(q--){
int di, si, w, x, y;
scanf("%d %d %d %d %d", &di, &si, &w, &x, &y);
x++, y++;
w += query(x, y, x+di-1, y+si-1, 1, d, 1);
update(x, y, x+di-1, y+si-1, w, 1, d, 1);
}
printf("%d\n", query(1, 1, d, s, 1, d, 1));
}
单点加,查询区间和
注意这份代码也可以满足区间加,查询区间和
int n; ll a[maxn][maxn];
struct seg{
ll t[maxn<<2], lz[maxn<<2];
void build(int L, int l, int r, int rt){
if(l == r) {
t[rt] = a[L][r];
return;
}
int m = l + r >> 1;
build(L, l, m, rt<<1);
build(L, m+1, r, rt<<1|1);
t[rt] = t[rt<<1] + t[rt<<1|1];
}
void update(int L, int R, ll x, int l, int r, int rt){
if(l>R || r<L) return;
t[rt] += 1ll * x * (R - L + 1);
if(l==L && r==R){
lz[rt] += x;
return;
}
int m = l + r >> 1;
if(m >= R) update(L, R, x, l, m, rt<<1);
else if(L > m) update(L, R, x, m+1, r, rt<<1|1);
else update(L, m, x, l, m, rt<<1), update(m+1, R, x, m+1, r, rt<<1|1);
}
ll query(int L, int R, int l, int r, int rt, ll sum){
if(l>R || r<L) return 0;
if(l==L && r==R) return t[rt] + sum * (r - l + 1);
sum += lz[rt];
int m = l + r >> 1;
if(m >= R) return query(L, R, l, m, rt<<1, sum);
else if(L > m) return query(L, R, m+1, r, rt<<1|1, sum);
else return query(L, m, l, m, rt<<1, sum) +
query(m+1, R, m+1, r, rt<<1|1, sum);
}
} t[maxn<<2], lz[maxn<<2];
void merge(seg p, seg p1, seg p2, int l, int r, int rt){
p.t[rt] = p1.t[rt] + p2.t[rt];
if(l == r) return;
int m = l + r >> 1;
merge(p, p1, p2, l, m, rt<<1);
merge(p, p1, p2, m+1, r, rt<<1|1);
}
void build(int l, int r, int rt){
if(l == r){
t[rt].build(l, 1, n, 1);
return;
}
int m = l + r >> 1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
merge(t[rt], t[rt<<1], t[rt<<1|1], 1, n, 1);
}
void update(int x, int y, int xx, int yy, ll v, int l, int r, int rt){
if(l>xx || r<x) return;
t[rt].update(y, yy, 1ll * v * (xx - x + 1), 1, n, 1);
if(l==x && r==xx){
lz[rt].update(y, yy, v, 1, n, 1);
return;
}
int m = l + r >> 1;
if(m >= xx) update(x, y, xx, yy, v, l, m, rt<<1);
else if(x > m) update(x, y, xx, yy, v, m+1, r, rt<<1|1);
else update(x, y, m, yy, v, l, m, rt<<1), \
update(m+1, y, xx, yy, v, m+1, r, rt<<1|1);
}
ll query(int x, int y, int xx, int yy, int l, int r, int rt, ll sum){
if(l>xx || r<x) return 0;
if(l==x && r==xx) return t[rt].query(y, yy, 1, n, 1, 0) + sum * (r - l + 1);
sum += lz[rt].query(y, yy, 1, n, 1, 0);
int m = l + r >> 1; ll ret = 0;
if(m >= xx) return query(x, y, xx, yy, l, m, rt<<1, sum);
else if(x > m) return query(x, y, xx, yy, m+1, r, rt<<1|1, sum);
else return query(x, y, m, yy, l, m, rt<<1, sum) +
query(m+1, y, xx, yy, m+1, r, rt<<1|1, sum) ;
}
int main() {
scanf("%d", &n);
while(1){
int m, x1, y1, x2, y2, w;
scanf("%d", &m);
if(m == 3) break;
if(m == 1){
scanf("%d%d%d", &x1, &y1, &w);
x1++, y1++, x2++, y2++;
update(x1, y1, x1, y1, w, 1, n, 1);
} else {
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
x1++, y1++, x2++, y2++;
printf("%lld\n", query(x1, y1, x2, y2, 1, n, 1, 0));
}
}
}
一:Treap
支持:插入、删除、查询 x x x 的排名、查询排名为 x x x 的数、查前驱、查后继、左旋右旋
int rt, tot;
int sz[maxn], val[maxn], cnt[maxn];
int rd[maxn], son[maxn][2];
inline void pushup(int x) {
sz[x] = sz[son[x][0]] + sz[son[x][1]] + cnt[x];
}
inline void rotate(int &x, int y) {
int ii = son[x][y^1];
son[x][y^1] = son[ii][y], son[ii][y] = x;
pushup(x), pushup(ii), x = ii;
}
void ins(int &p, int x) {
if(!p) {
p = ++tot, sz[p] = cnt[p] = 1;
val[p] = x, rd[p] = rand();
return;
}
if(val[p] == x) return cnt[p]++, sz[p]++, void();
int d = x > val[p]; ins(son[p][d], x);
if(rd[p] < rd[son[p][d]]) rotate(p, d^1);
pushup(p);
}
void del(int &p, int x) {
if(!p) return;
if(x != val[p]) del(son[p][x>val[p]], x);
else {
if(!son[p][0] && !son[p][1]) {
cnt[p]--, sz[p]--;
if(cnt[p] == 0) p = 0;
} else if(son[p][0] && !son[p][1]) {
rotate(p, 1), del(son[p][1], x);
} else if(!son[p][0] && son[p][1]) {
rotate(p, 0), del(son[p][0], x);
} else {
int d = rd[son[p][0]] > rd[son[p][1]];
rotate(p, d), del(son[p][d], x);
}
}
pushup(p);
}
int get_rank(int p, int x) {
if(!p) return 0;
if(val[p] == x) return sz[son[p][0]] + 1;
if(val[p] < x) return sz[son[p][0]] + cnt[p] + get_rank(son[p][1], x);
return get_rank(son[p][0], x); // val[p]>x
}
int kth(int p, int x) {
if(!p) return 0;
if(sz[son[p][0]] >= x) return kth(son[p][0], x);
else if(sz[son[p][0]] + cnt[p] < x)
return kth(son[p][1], x-cnt[p]-sz[son[p][0]]);
else return val[p];
}
int pre(int p, int x) {
if(!p) return -INF;
if(val[p] >= x) return pre(son[p][0], x);
else return max(val[p], pre(son[p][1], x));
}
int suf(int p, int x) {
if(!p) return INF;
if(val[p] <= x) return suf(son[p][1], x);
else return min(val[p], suf(son[p][0], x));
}
signed main() {
int Q;
scanf("%d", &Q);
while(Q--) {
int opt, x;
scanf("%d%d", &opt, &x);
if(opt == 1) ins(rt, x);
else if(opt == 2) del(rt, x);
else if(opt == 3) printf("%d\n", get_rank(rt, x));
else if(opt == 4) printf("%d\n", kth(rt, x));
else if(opt == 5) printf("%d\n", pre(rt, x));
else if(opt == 6) printf("%d\n", suf(rt, x));
}
}
二:FHQ Treap
支持:按权值
v
a
l
val
val 分割(维护
v
a
l
val
val 的有序性)、按排名即
s
i
z
e
size
size 分割(维护序列的有序性)、合并两颗树
插入、删除、查询
x
x
x 的排名、查询排名为
x
x
x 的数、查前驱、查后继
区间左移 —— HDU多校第九场 1007 Game
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
struct Treap {
const static int maxn = 1e5 + 5;
int root, tot;
int sz[maxn], val[maxn];
int rd[maxn], son[maxn][2];
void init() {
fill(sz, sz+tot+1, 0);
fill(son[0], son[0]+tot+1, 0);
fill(son[1], son[1]+tot+1, 0);
root = tot = 0;
}
Treap() {
root = tot = 0;
// srand(clock() + time(0));
}
inline void pushup(int x) {
sz[x] = sz[son[x][0]] + sz[son[x][1]] + 1;
}
inline int new_node(int v) {
sz[++tot] = 1, val[tot] = v, rd[tot] = rnd();
return tot;
}
int merge(int x, int y) {
if(!x || !y) return x + y;
if(rd[x] < rd[y]) return son[x][1] = merge(son[x][1], y), pushup(x), x;
else return son[y][0] = merge(x, son[y][0]), pushup(y), y;
}
void split_by_val(int now, int k, int &x, int &y) {
if(!now) return x = y = 0, void();
if(val[now] <= k) x = now, split_by_val(son[now][1],\
k, son[now][1], y);
else y = now, split_by_val(son[now][0], k, x, son[now][0]);
pushup(now);
}
void split_by_sz(int now, int k, int &x, int &y) {
if(!now) return x = y = 0, void();
if(sz[son[now][0]] >= k) y = now, \
split_by_sz(son[now][0], k, x, son[now][0]);
else x = now, split_by_sz(son[now][1], \
k-sz[son[now][0]]-1, son[now][1], y);
pushup(now);
}
void ins(int v) {
int x, y; split_by_val(root, v, x, y);
root = merge(merge(x, new_node(v)), y);
}
void del(int v) {
int x, y, z;
split_by_val(root, v, x, z);
split_by_val(x, v-1, x, y);
y = merge(son[y][0], son[y][1]);
root = merge(merge(x, y), z);
}
inline int get_rank(int v) {
int x, y, ret;
split_by_val(root, v-1, x, y);
ret = sz[x] + 1;
root = merge(x, y);
return ret;
}
inline int kth(int now, int k) { // 查询排名
while(true) {
if(k <= sz[son[now][0]]) now = son[now][0];
else if(k == sz[son[now][0]] + 1) return val[now];
else k -= sz[son[now][0]] + 1, now = son[now][1];
}
}
inline int pre(int v) {
int x, y, ret;
split_by_val(root, v-1, x, y);
ret = kth(x, sz[x]);
root = merge(x, y);
return ret;
}
inline int suf(int v) {
int x, y, ret;
split_by_val(root, v, x, y);
ret = kth(y, 1);
root = merge(x, y);
return ret;
}
} T;
signed main() {
int Q;
scanf("%d", &Q);
while(Q--) {
int opt, x;
scanf("%d%d", &opt, &x);
if(opt == 1) T.ins(x);
else if(opt == 2) T.del(x);
else if(opt == 3) printf("%d\n", T.get_rank(x));
else if(opt == 4) printf("%d\n", T.kth(T.root, x));
else if(opt == 5) printf("%d\n", T.pre(x));
else if(opt == 6) printf("%d\n", T.suf(x));
}
}
三:文艺平衡树
维护一个序列,实现区间翻转
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
int b[maxn];
struct Treap {
const static int maxn = 5e5 + 5;
int root, tot;
int sz[maxn], val[maxn], lz[maxn];
int rd[maxn], son[maxn][2];
Treap() {
root = tot = 0;
}
inline void init() {
fill(sz, sz+tot+1, 0);
fill(son[0], son[0]+tot+1, 0);
fill(son[1], son[1]+tot+1, 0);
root = tot = 0;
}
inline void pushup(int x) {
sz[x] = sz[son[x][0]] + sz[son[x][1]] + 1;
}
inline void pushdown(int x) {
swap(son[x][0], son[x][1]);
if(son[x][0]) lz[son[x][0]] ^= 1;
if(son[x][1]) lz[son[x][1]] ^= 1;
lz[x] = 0;
}
int merge(int x, int y) {
if(!x || !y) return x + y;
if(rd[x] < rd[y]) {
if(lz[x]) pushdown(x);
return son[x][1] = merge(son[x][1], y), pushup(x), x;
}
else {
if(lz[y]) pushdown(y);
return son[y][0] = merge(x, son[y][0]), pushup(y), y;
}
}
void split_by_sz(int now, int k, int &x, int &y) {
if(!now) return x = y = 0, void();
if(lz[now]) pushdown(now);
if(sz[son[now][0]] >= k) y = now, split_by_sz(son[now][0],\
k, x, son[now][0]);
else x = now, split_by_sz(son[now][1],\
k-sz[son[now][0]]-1, son[now][1], y);
pushup(now);
}
void insert(int pos, int v) {
int u = ++tot, x, y; val[u] = v, sz[u] = 1, rd[u] = rnd();
split_by_sz(root, pos-1, x, y);
root = merge(merge(x, u), y);
}
int dfs(int *a, int l, int r) {
if(l > r) return 0;
int u = ++tot, mid = l + r >> 1;
val[u] = a[mid], sz[u] = r - l + 1, rd[u] = rnd();
son[u][0] = dfs(a, l, mid-1); son[u][1] = dfs(a, mid+1, r);
pushup(u); return u;
}
void build(int *a, int l, int r) {
root = dfs(a, l, r);
}
void dfs_seq(int u, int *p) {
if(u == 0) return;
if(lz[u]) pushdown(u);
dfs_seq(son[u][0], p);
*(p + sz[son[u][0]] + 1) = val[u];
dfs_seq(son[u][1], p+sz[son[u][0]]+1);
}
void seq(int *p) {
dfs_seq(root, p);
}
inline int kth(int now, int k) { // 查询排名
while(true) {
if(k <= sz[son[now][0]]) now = son[now][0];
else if(k == sz[son[now][0]] + 1) return val[now];
else k -= sz[son[now][0]] + 1, now = son[now][1];
}
}
void reverse(int l, int r) {
int L, R, l1, r1;
split_by_sz(root, r, L, R);
split_by_sz(L, l-1, l1, r1);
lz[r1] ^= 1;
root = merge(merge(l1, r1), R);
}
} T;
signed main() {
int n, m, l, r;
scanf("%d%d", &n, &m);
// for(int i=1; i<=n; i++) T.insert(i, i);
for(int i=1; i<=n; i++) b[i] = i;
T.build(b, 1, n);
while(m--) {
scanf("%d%d", &l, &r);
T.reverse(l, r);
}
T.seq(b);
for(int i=1; i<=n; i++) printf("%d%c", b[i], " \n"[i==n]);
}
题目大意:
n
n
n 列木块,每列都有高度,两种操作:
1.
1.
1. 选择一个点
(
x
,
y
)
(x, y)
(x,y),从右往左推一格,有些格子会受到重力影响往下掉,并输出移动的木块数
2.
2.
2. 查询第
x
x
x 列的高度
最后输出所有列的高度
解题思路:
具体操作就是查询区间小于
y
y
y 的最右点,然后区间左移
因此平衡树要维护区间最小值和区间和,然后就可以树内二分查了
区间左移需要先将这个区间给分割出来,修改边界点
合并的时候就将区间左端点和后面区间交换一下
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
int b[maxn];
struct Treap {
const static int maxn = 5e5 + 5;
int root, tot; ll sum[maxn];
int sz[maxn], val[maxn], mn[maxn];
int rd[maxn], son[maxn][2];
Treap() {
root = tot = 0;
mn[0] = 1e9, sum[0] = val[0] = 0;
}
inline void init() {
fill(sz, sz+tot+1, 0);
fill(son[0], son[0]+tot+1, 0);
fill(son[1], son[1]+tot+1, 0);
root = tot = 0;
}
inline void pushup(int x) {
sz[x] = sz[son[x][0]] + sz[son[x][1]] + 1;
mn[x] = min(val[x], min(mn[son[x][0]], mn[son[x][1]]));
sum[x] = sum[son[x][0]] + sum[son[x][1]] + val[x];
}
void split_by_min(int now, int k, int &x, int &y) { // 二分
if(!now) return x = y = 0, void();
if(mn[son[now][1]]<k || val[now]<k) x = now, split_by_min(\
son[now][1], k, son[now][1], y);
else y = now, split_by_min(son[now][0], k, x, son[now][0]);
pushup(now);
}
int dfs(int *a, int l, int r) {
if(l > r) return 0;
int u = ++tot, mid = l + r >> 1;
sum[u] = val[u] = mn[u] = a[mid], sz[u] = r - l + 1, rd[u] = rnd();
son[u][0] = dfs(a, l, mid-1); son[u][1] = dfs(a, mid+1, r);
pushup(u); return u;
}
void build(int *a, int l, int r) {
root = dfs(a, l, r);
}
void dfs_seq(int u, int *p){
if(u == 0) return;
dfs_seq(son[u][0], p);
*(p + sz[son[u][0]] + 1) = val[u];
dfs_seq(son[u][1], p+sz[son[u][0]]+1);
}
void seq(int *p) {
dfs_seq(root, p);
}
ll gao(int x, int y) {
if(kth(root, x) < y) return 0;
int L, R;
split_by_sz(root, x, L, R);
if(mn[L] >= y) return root = merge(L, R), 0;
int l, r, l1, l2, r1, r2;
split_by_min(L, y, l, r);
ll ret = sum[r] - 1ll * sz[r] * (y - 1);
split_by_sz(l, sz[l]-1, l1, l2);
split_by_sz(r, 1, r1, r2);
val[l2] += val[r1] - (y - 1);
sum[l2] = mn[l2] = val[l2];
sum[r1] = mn[r1] = val[r1] = y - 1;
root = merge(merge(merge(l1, l2), merge(r2, r1)), R);
return ret;
}
} T;
signed main() {
int Q, n, q, op, x, y;
scanf("%d", &Q);
while(Q--) {
T.init();
scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++) scanf("%d", b+i);
T.build(b, 1, n);
while(q--) {
scanf("%d", &op);
if(op == 1) scanf("%d%d", &x, &y), printf("%lld\n", T.gao(x, y));
else scanf("%d", &x), printf("%d\n", T.kth(T.root, x));
}
T.seq(b);
for(int i=1; i<=n; i++) printf("%d%c", b[i], " \n"[i==n]);
}
}
int n, m, a[maxn][maxn];
int head, tail, q[maxn];
int main() {
scanf("%d%d", &n, &m);
ll ans = 0;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++) {
scanf("%d", &a[i][j]);
a[i][j] = a[i][j] ? a[i-1][j]+1 : 0;
}
for(int i=1; i<=n; i++) {
head = 1, tail = 0;
for(int j=1; j<=m; j++) {
while(head<=tail && a[i][j]<=a[i][q[tail]]) {
ll hei = a[i][q[tail]];
ll wid = j - q[tail-1] - 1;
ans = max(ans, hei * wid);
tail--;
}
q[++tail] = j;
}
while(head <= tail) {
ll hei = a[i][q[tail]];
ll wid = m - q[tail-1];
ans = max(ans, hei * wid);
tail--;
}
}
printf("%lld\n", ans);
}
操作:
x x x & \& & ( x − 1 ) (x - 1) (x−1) | 判断是否为 2 2 2 的幂,为 0 0 0 时表示 x x x 是 2 2 2的幂 |
---|---|
x x x & = \&= &= ( x − 1 ) (x - 1) (x−1) | 消去最后一位 b i t bit bit |
x x x & \& & ( − x ) (-x) (−x) | 取出最后一位 b i t bit bit |
思想:
c
[
i
]
c[i]
c[i] 表示第
i
i
i 个元素向前数
l
o
w
b
i
t
(
i
)
lowbit(i)
lowbit(i) 个元素
查询前缀时,不断取
l
o
w
b
i
t
lowbit
lowbit,即可得到前缀有哪几个
c
[
i
]
c[i]
c[i] 组成
更新单点时,不断更新它的直系父层,也就是
+
=
l
o
w
b
i
t
(
i
)
+=lowbit(i)
+=lowbit(i)
int n, c[50010];
inline int lowbit(int x) {
return x & (-x);
}
int sum(int x) {
int ret = 0;
for (int i=x; i; i-=lowbit(i))
ret += c[i];
return ret;
}
void add(int x, int val) {
for (int i=x; i<=n; i+=lowbit(i))
c[i] += val;
}
求第k小:
从大到小枚举
2
2
2 的幂
若当前的前缀和
<
k
<k
<k
则答案在当前位置的右边
注意最后返回的时候要
+
1
+1
+1
int n, q, c[maxn];
void add(int x, int v){
for(int i=x; i<=n; i+=(i&-i))
c[i] += v;
}
int query(int x){
if(!x) return 0;
int ptr = 0;
for(int i=20; ~i; i--){
if(ptr+(1<<i)<=n && c[ptr+(1<<i)]<x){
x -= c[ptr + (1<<i)];
ptr += (1<<i);
}
}
return ptr + 1;
}
signed main() {
scanf("%d%d", &n, &q);
for(int i=1, x; i<=n; i++) scanf("%d", &x), add(x, 1);
int cnt = n;
for(int i=1, x; i<=q; i++){
scanf("%d", &x);
if(x > 0) add(x, 1), cnt++;
else {
int pos = query(-x);
add(pos, -1);
cnt--;
}
}
printf("%d\n", cnt>0 ? query(1) : 0);
}
定义
对于一个简单的序列:
9
,
3
,
7
,
1
,
8
,
12
,
10
,
20
,
15
,
18
,
5
9, 3, 7, 1, 8, 12, 10, 20, 15, 18, 5
9,3,7,1,8,12,10,20,15,18,5
p
o
s
pos
pos 表示原序列中的位置,
v
a
l
val
val 表示该位置的值
笛卡尔树具有如下两个性质:
①
:
p
o
s
①:pos
①:pos 满足二叉搜索树,左子树的
p
o
s
pos
pos 小于根,右子树的
p
o
s
pos
pos 大于根
②
:
v
a
l
②:val
②:val 满足堆,左右子树的
v
a
l
val
val 大于根
建树
按
i
=
1
~
n
i=1~ n
i=1~n 的顺序依次加入节点并调整树的结构,使得当前的树为子序列
[
1
,
i
]
[1,i]
[1,i] 所构成的笛卡尔树
若当前插入节点
i
i
i,因为
i
i
i 在区间
[
1
,
i
]
[1,i]
[1,i] 中
p
o
s
pos
pos 最大,
p
o
s
pos
pos满足二叉搜索树,因此
i
i
i 在根节点一直向右儿子,走到空节点
然后考虑
i
i
i 的
v
a
l
val
val 值,由于
v
a
l
val
val 满足堆,因此只需要将
i
i
i 拼命往上移动,找到一个祖先
j
j
j,满足
j
j
j 的子树的
v
a
l
val
val 全大于
i
i
i 的
v
a
l
val
val
然后
i
i
i 就成为了
j
j
j 的父亲,
j
j
j 成了
i
i
i 的 左子树
并且
i
i
i 要继承
j
j
j 的父亲,
i
i
i 成为它原来父亲的 右儿子
这个过程可以用单调栈解决
时间复杂度: O ( n ) O(n) O(n)
一些性质:
①
:
①:
①:下标
i
i
i 到下标
j
j
j 之间
(
i
<
j
)
(i<j)
(i<j) 的最小值,只需寻找
i
i
i 与
j
j
j 的
l
c
a
lca
lca
②
:
②:
②:以
i
i
i 为最小值的区间长度
=
i
= i
=i 的子树大小,左端点为子树中
p
o
s
pos
pos 最小值,右端点为
p
o
s
pos
pos 最大值
③
:
③:
③:分治处理到
[
l
,
r
]
[l,r]
[l,r] 时,若最小值为
a
i
a_i
ai,
i
i
i 即为此区间对应子树的根节点,相当于在笛卡尔树上递归分治
然后将区间分为
[
l
,
i
−
1
]
,
[
i
+
1
,
r
]
[l,i-1], [i+1,r]
[l,i−1],[i+1,r] 两部分,
[
l
,
i
−
1
]
[l,i-1]
[l,i−1] 的最小值就是
a
l
s
[
i
]
a_{ls[i]}
als[i],
[
i
+
1
,
r
]
[i+1,r]
[i+1,r] 的最小值为
a
r
s
[
i
]
a_{rs[i]}
ars[i]
模板
① : ①: ①:下标为 1 ~ n 1 ~ n 1~n
int st[maxn], fa[maxn];
int ls[maxn], rs[maxn];
void build() {
int tail = 0;
for(int i=1; i<=n; i++) {
while(tail && a[st[tail]] > a[i]) ls[i] = st[tail], tail--;
fa[i] = st[tail], fa[ls[i]] = i;
if(fa[i]) rs[fa[i]] = i;
st[++tail] = i;
}
}
② : ②: ②: 下标为 0 ~ n − 1 0 ~ n-1 0~n−1
int st[maxn], fa[maxn];
int ls[maxn], rs[maxn];
void build() {
int tail = 0;
for(int i=0; i<n; i++) {
int t = tail;
while(t && a[st[t]] > a[i]) t--;
if(t < tail) ls[i] = st[t+1], fa[ls[i]] = i;
if(t) rs[st[t]] = i, fa[i] = st[t];
st[++t] = i; tail = t;
}
}
多次操作,支持查询曼哈顿距离最近的点 与 插入一个点
const double alpha = 0.75;
int n, m, rt, tot, now, ans;
int sta[maxn], top;
struct node{
int pla[2];
} a[maxn];
struct tree{
int mx[2], mn[2], sz, ls, rs;
node place;
} t[maxn];
inline int New(){
if(top) return sta[top--];
return ++tot;
}
inline bool cmp(const node &A, const node &B){
return A.pla[now] < B.pla[now];
}
inline void update(int p){
for(int i=0; i<=1; i++){
t[p].mx[i] = t[p].mn[i] = t[p].place.pla[i];
if(t[p].ls) t[p].mx[i] = max(t[p].mx[i], t[t[p].ls].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].ls].mn[i]);
if(t[p].rs) t[p].mx[i] = max(t[p].mx[i], t[t[p].rs].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].rs].mn[i]);
}
t[p].sz = t[t[p].ls].sz + t[t[p].rs].sz + 1;
}
inline int build(int l, int r, int opt){
if(l > r) return 0;
int x = New(), mid = l + r >> 1; now = opt;
nth_element(a+l, a+mid, a+r+1, cmp); t[x].place = a[mid];
t[x].ls = build(l, mid-1, opt^1);
t[x].rs = build(mid+1, r, opt^1);
update(x); return x;
}
inline void pia(int p, int cnt){
if(t[p].ls) pia(t[p].ls, cnt);
a[cnt+t[t[p].ls].sz+1] = t[p].place, sta[++top] = p;
if(t[p].rs) pia(t[p].rs, cnt+t[t[p].ls].sz+1);
}
inline void check(int &p, int opt){
if(t[p].sz*alpha<t[t[p].ls].sz || t[p].sz*alpha<t[t[p].rs].sz)
pia(p, 0), p = build(1, t[p].sz, opt);
}
inline void insert(node ret, int &p, int opt){
if(!p){
p = New(); t[p].place = ret;
t[p].ls = t[p].rs = 0;
update(p); return;
}
if(ret.pla[opt] <= t[p].place.pla[opt]) insert(ret, t[p].ls, opt^1);
else insert(ret, t[p].rs, opt^1);
update(p); check(p, opt);
}
inline int getdis(node a, int p){
int ret = 0;
for(int i=0; i<=1; i++)
ret += max(0, a.pla[i]-t[p].mx[i]) + max(0, t[p].mn[i]-a.pla[i]);
return ret;
}
inline int dis(node a, node b){
return abs(a.pla[0]-b.pla[0]) + abs(a.pla[1]-b.pla[1]);
}
inline void query(node ret, int p){
ans = min(ans, dis(ret, t[p].place));
int tmpl = inf, tmpr = inf;
if(t[p].ls) tmpl = getdis(ret, t[p].ls);
if(t[p].rs) tmpr = getdis(ret, t[p].rs);
if(tmpl < tmpr){
if(tmpl < ans) query(ret, t[p].ls);
if(tmpr < ans) query(ret, t[p].rs);
} else {
if(tmpr < ans) query(ret, t[p].rs);
if(tmpl < ans) query(ret, t[p].ls);
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) scanf("%d%d", &a[i].pla[0], &a[i].pla[1]);
rt = build(1, n, 0);
for(int i=1; i<=m; i++){
int opt; node ret;
scanf("%d%d%d", &opt, &ret.pla[0], &ret.pla[1]);
if(opt == 1) insert(ret, rt, 0);
else ans = inf, query(ret, rt), printf("%d\n", ans);
}
}
用
K
D
−
t
r
e
e
KD - tree
KD−tree 维护一个矩阵最值、单点查询、矩阵和
支持矩阵覆盖,满足了树套树无法
p
u
s
h
d
o
w
n
pushdown
pushdown 的短处
矩阵覆盖、查询矩阵最值
const double alpha = 0.75;
int n, m, rt, tot, now, ans;
struct node{
int pla[2], val;
} a[maxn];
struct tree{
int mx[2], mn[2], sz, ls, rs;
int lz, val, ans;
node place;
} t[maxn];
inline int New(){
return ++tot;
}
inline bool cmp(const node &A, const node &B){
return A.pla[now] < B.pla[now];
}
inline void update(int p){
for(int i=0; i<=1; i++){
t[p].mx[i] = t[p].mn[i] = t[p].place.pla[i];
if(t[p].ls) t[p].mx[i] = max(t[p].mx[i], t[t[p].ls].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].ls].mn[i]);
if(t[p].rs) t[p].mx[i] = max(t[p].mx[i], t[t[p].rs].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].rs].mn[i]);
}
t[p].ans = max(t[p].ans, max(t[t[p].ls].ans, t[t[p].rs].ans));
// t[p].sz = t[t[p].ls].sz + t[t[p].rs].sz + 1;
}
inline int build(int l, int r, int opt){
if(l > r) return 0;
int x = New(), mid = l + r >> 1; now = opt;
nth_element(a+l, a+mid, a+r+1, cmp);
t[x].place = a[mid], t[x].lz = 0;
t[x].val = t[x].ans = a[mid].val;
t[x].ls = build(l, mid-1, opt^1);
t[x].rs = build(mid+1, r, opt^1);
update(x); return x;
}
inline void pushdown(int p){
if(!t[p].lz) return;
if(t[p].ls) t[t[p].ls].lz = t[t[p].ls].val = t[t[p].ls].ans = t[p].lz;
if(t[p].rs) t[t[p].rs].lz = t[t[p].rs].val = t[t[p].rs].ans = t[p].lz;
t[p].lz = 0;
}
inline void update(int p, int x1, int y1, int x2, int y2, int w){
if(t[p].mx[0]<x1 || t[p].mn[0]>x2) return;
if(t[p].mx[1]<y1 || t[p].mn[1]>y2) return;
pushdown(p);
if(t[p].place.pla[0]>=x1 && t[p].place.pla[0]<=x2 \
&& t[p].place.pla[1]>=y1 && t[p].place.pla[1]<=y2)
t[p].val = t[p].ans = w;
if(t[p].mn[0]>=x1 && t[p].mx[0]<=x2 \
&& t[p].mn[1]>=y1 && t[p].mx[1]<=y2){
t[p].lz = t[p].val = t[p].ans = w;
return;
}
if(t[p].ls) update(t[p].ls, x1, y1, x2, y2, w);
if(t[p].rs) update(t[p].rs, x1, y1, x2, y2, w);
t[p].ans = max(t[p].ans, max(t[t[p].ls].ans, t[t[p].rs].ans));
}
inline void query(int p, int x1, int y1, int x2, int y2){
if(!p || t[p].ans<ans) return ;
if(t[p].mx[0]<x1 || t[p].mn[0]>x2) return ;
if(t[p].mx[1]<y1 || t[p].mn[1]>y2) return ;
pushdown(p);
if(t[p].place.pla[0]>=x1 && t[p].place.pla[0]<=x2 \
&& t[p].place.pla[1]>=y1 && t[p].place.pla[1]<=y2)
ans = max(ans, t[p].val);
query(t[p].ls, x1, y1, x2, y2);
query(t[p].rs, x1, y1, x2, y2);
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++){
scanf("%d", &a[(i-1)*n+j].val);
a[(i-1)*n+j].pla[0] = i;
a[(i-1)*n+j].pla[1] = j;
}
rt = build(1, n*n, 0);
for(int i=1; i<=m; i++){
int x1, y1, x2, y2, w, op;
scanf("%d", &op);
if(op == 1){
scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &w);
update(rt, x1, y1, x2, y2, w);
} else {
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
ans = 0; query(rt, x1, y1, x2, y2);
printf("%d\n", ans);
}
}
}
单点加、查询矩阵和
注意也可用 二维线段树做
int n, m, rt, tot, now, ans, cnt;
int sta[maxn], top;
struct node{
int pla[2], val;
} a[maxn];
struct tree{
int mx[2], mn[2], sz, ls, rs;
int val, sum;
node place;
} t[maxn];
inline int New(){
if(top) return sta[top--];
return ++tot;
}
inline bool cmp(const node &A, const node &B){
return A.pla[now] < B.pla[now];
}
inline void update(int p){
for(int i=0; i<=1; i++){
t[p].mx[i] = t[p].mn[i] = t[p].place.pla[i];
if(t[p].ls) t[p].mx[i] = max(t[p].mx[i], t[t[p].ls].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].ls].mn[i]);
if(t[p].rs) t[p].mx[i] = max(t[p].mx[i], t[t[p].rs].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].rs].mn[i]);
}
t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum + t[p].val; // 注意是更新!!
t[p].sz = t[t[p].ls].sz + t[t[p].rs].sz + 1;
}
inline int build(int l, int r, int opt){
if(l > r) return 0;
int x = New(), mid = l + r >> 1; now = opt;
nth_element(a+l, a+mid, a+r+1, cmp);
t[x].place = a[mid], t[x].val = t[x].sum = a[mid].val;
t[x].ls = build(l, mid-1, opt^1);
t[x].rs = build(mid+1, r, opt^1);
update(x); return x;
}
inline void pia(int p, int cnt){
if(t[p].ls) pia(t[p].ls, cnt);
a[cnt+t[t[p].ls].sz+1] = t[p].place, sta[++top] = p;
if(t[p].rs) pia(t[p].rs, cnt+t[t[p].ls].sz+1);
}
inline void check(int &p, int opt){
if(t[p].sz*0.75<t[t[p].ls].sz || t[p].sz*0.75<t[t[p].rs].sz)
pia(p, 0), p = build(1, t[p].sz, opt);
}
inline void insert(node ret, int &p, int opt){
if(!p){
p = New(); t[p].place = ret;
t[p].val = t[p].sum = ret.val;
t[p].ls = t[p].rs = 0;
update(p); return;
}
if(ret.pla[opt] <= t[p].place.pla[opt]) insert(ret, t[p].ls, opt^1);
else insert(ret, t[p].rs, opt^1);
update(p); check(p, opt);
}
inline int query(int p, int x1, int y1, int x2, int y2){
if(!p) return 0;
if(t[p].mx[0]<x1 || t[p].mn[0]>x2) return 0;
if(t[p].mx[1]<y1 || t[p].mn[1]>y2) return 0;
if(t[p].mn[0]>=x1 && t[p].mx[0]<=x2 \
&& t[p].mn[1]>=y1 && t[p].mx[1]<=y2)
return t[p].sum;
int ret = 0;
if(t[p].place.pla[0]>=x1 && t[p].place.pla[0]<=x2 \
&& t[p].place.pla[1]>=y1 && t[p].place.pla[1]<=y2)
ret += t[p].val;
ret += query(t[p].ls, x1, y1, x2, y2);
ret += query(t[p].rs, x1, y1, x2, y2);
return ret;
}
signed main() {
scanf("%d", &n);
int op, x1, y1, x2, y2, lans = 0;
while(true){
scanf("%d", &op);
if(op == 1){
cnt++;
scanf("%d%d%d", &a[cnt].pla[0], &a[cnt].pla[1], &a[cnt].val);
a[cnt].pla[0] ^= lans, a[cnt].pla[1] ^= lans, a[cnt].val ^= lans;
insert(a[cnt], rt, 0);
// if(cnt % 10000 == 0) tot = 0, rt = build(1, cnt, 0);
} else if(op == 2){
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
x1 ^= lans, y1 ^= lans, x2 ^= lans, y2 ^= lans;
printf("%d\n", lans = query(rt, x1, y1, x2, y2));
} else break;
}
}
矩阵覆盖、查询单点
const double alpha = 0.75;
int n, rt, tot, now;
int T, c, q, dfntot;
int dfn[maxn], out[maxn], dep[maxn];
vector <int> g[maxn];
struct node{
int pla[2], val;
} a[maxn];
struct tree{
int mx[2], mn[2], sz, ls, rs;
int lz, val, ans;
node place;
} t[maxn];
inline int New(){
return ++tot;
}
inline bool cmp(const node &A, const node &B){
return A.pla[now] < B.pla[now];
}
inline void update(int p){
for(int i=0; i<=1; i++){
t[p].mx[i] = t[p].mn[i] = t[p].place.pla[i];
if(t[p].ls) t[p].mx[i] = max(t[p].mx[i], t[t[p].ls].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].ls].mn[i]);
if(t[p].rs) t[p].mx[i] = max(t[p].mx[i], t[t[p].rs].mx[i]), \
t[p].mn[i] = min(t[p].mn[i], t[t[p].rs].mn[i]);
}
// t[p].sz = t[t[p].ls].sz + t[t[p].rs].sz + 1;
}
inline int build(int l, int r, int opt){
if(l > r) return 0;
int x = New(), mid = l + r >> 1; now = opt;
nth_element(a+l, a+mid, a+r+1, cmp);
t[x].place = a[mid], t[x].val = a[mid].val, t[x].lz = 0;
t[x].ls = build(l, mid-1, opt^1);
t[x].rs = build(mid+1, r, opt^1);
update(x); return x;
}
inline void pushdown(int p){
if(!t[p].lz) return;
if(t[p].ls) t[t[p].ls].lz = t[t[p].ls].val = t[p].lz;
if(t[p].rs) t[t[p].rs].lz = t[t[p].rs].val = t[p].lz;
t[p].lz = 0;
}
inline void update(int p, int x1, int y1, int x2, int y2, int w){
if(!p) return;
if(t[p].mx[0]<x1 || t[p].mn[0]>x2) return;
if(t[p].mx[1]<y1 || t[p].mn[1]>y2) return;
pushdown(p);
if(t[p].place.pla[0]>=x1 && t[p].place.pla[0]<=x2 \
&& t[p].place.pla[1]>=y1 && t[p].place.pla[1]<=y2)
t[p].val = w;
if(t[p].mn[0]>=x1 && t[p].mx[0]<=x2 \
&& t[p].mn[1]>=y1 && t[p].mx[1]<=y2){
t[p].lz = t[p].val = w;
return;
}
if(t[p].ls) update(t[p].ls, x1, y1, x2, y2, w);
if(t[p].rs) update(t[p].rs, x1, y1, x2, y2, w);
}
inline int query(int p, int x1, int y1, int x2, int y2){
if(!p) return 0;
if(t[p].mx[0]<x1 || t[p].mn[0]>x2) return 0;
if(t[p].mx[1]<y1 || t[p].mn[1]>y2) return 0;
pushdown(p);
if(t[p].place.pla[0]>=x1 && t[p].place.pla[0]<=x2 \
&& t[p].place.pla[1]>=y1 && t[p].place.pla[1]<=y2)
return t[p].val;
int ret = 0;
ret = max(ret, query(t[p].ls, x1, y1, x2, y2));
ret = max(ret, query(t[p].rs, x1, y1, x2, y2));
return ret;
}
void dfs(int u, int de){
dfn[u] = ++dfntot, dep[u] = de, a[u].val = 1;;
a[u].pla[0] = dfn[u], a[u].pla[1] = de;
// for(auto v : g[u]) dfs(v, de+1);
for(int v=0; v<g[u].size(); v++) dfs(g[u][v], de+1);
out[u] = dfntot;
}
int main() {
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &n, &c, &q);
for(int i=1; i<=n; i++) g[i].clear();
for(int i=2, f; i<=n; i++){
scanf("%d", &f);
g[f].push_back(i);
}
dfntot = 0; dfs(1, 0); tot = 0;
rt = build(1, n, 0);
int qa, ql, qc, ans = 0;
for(int i=1; i<=q; i++) {
scanf("%d%d%d", &qa, &ql, &qc);
if(qc) update(rt, dfn[qa], dep[qa], out[qa], dep[qa]+ql, qc);
else {
int tans = query(rt, dfn[qa], dep[qa], dfn[qa], dep[qa]);
ans = (1ll * tans * i % mod + ans) % mod;
}
}
printf("%d\n", ans);
}
}