洛谷 P9530 Fish 2
赛时怎么想的呢,想建大根笛卡尔树。
根肯定是可以吃掉整个子树的,那看看子树里的点能不能吃掉根。如果整个子树的和都小于根,那么这个子树就没救了,标记上。否则往下递归。
这样是高妙的 \(O(nq)\) 暴力!
定义【禁止区间】为一个区间 \([l, r]\) 满足 \(\min(a_l, a_r) > \sum_{i=l+1}^{r-1} a_i\)。当然 \(l \ge 0, r \le n+1, l+2 \le r\)。并且额外定义 \(a_0=a_{n+1}=+\infty\)。
显然一条鱼能活,当且仅当它没有被禁止区间覆盖到。具体见查询。
显然禁止区间不相交。然后最劣就有这些禁止区间构成一个类似线段树的样子,所以禁止区间的总个数是 \(O(n)\)。
然后考察以 \(l\) 为左端点的禁止区间个数。发现若 \(r_1\) 和 \(r_2\) 都是合法的右端点,则 \(\sum_{i=l+1}^{r_2-1} \ge \sum_{i=l+1}^{r_1} = a_{r_1} +\sum_{i=l+1}^{r_1-1} > 2\sum_{i=l+1}^{r_1-1}\)。扩倍!所以只有 \(O(\log a)\) 个可能的右端点。
记 \(sum\) 为 \(a\) 的前缀和。
禁止区间必须满足 \(a_r > sum_{r-1} - sum_l\)。类似地,这个放松过的条件,仍然有一个 \(l\) 最多对应 \(O(\log a)\) 个区间!
包括钦定一个点 \(i\),要求 \(l \le i \le r\) 的禁止区间,也是只有 \(O(\log V)\) 个的。
提一嘴,如何求解这 \(i\) 对应的禁止区间?
要想使得 \(a_r > sum_{r-1} - sum_l\),一个必要条件是 \(a_r >sum_{r-1} - sum_i\)。这样右端点就只有 \(O(\log a )\) 个。左端点同理。
求解的话
首先,如何求解这些禁止区间?
枚举左端点 \(l\),找到所有的 \(O(\log a)\) 个右端点是不难的。建立一棵线段树,每个结点存储子区间中 \(sum_{i-1} - a_i\) 的最小值。在递归分成左右两个区间的过程中,如果某个分支的 \(\min\) 小于 \(sum_l\) 就进入。
找到这些 \(r\) 之后再暴力判断 \(a_l > sum_{r-1} - sum_l\) 是否成立即可。
复杂度是 \(O(n\log n\log a)\) 的。
当然,枚举 \(r\) 找 \(l\) 也是同理的。
然后考虑查询 \([x, y]\)。
查询是需要将所有严格包含 \([x, y]\) 的禁止区间都删除的。
用一棵线段树支持区间加、查询区间 \(\min\) 个数即可快速数出多少个位置没有被禁止区间覆盖到。
但是提取出来的 \([x, y]\),可能会产生一个新的前缀禁止区间和一个新的后缀禁止区间。用上面的方法暴力查找就可以。
最后看看修改。修改一个值,首先不会影响所有与 \(x\) 不交的禁止区间。
然后需要的就是删掉包含 \(x\) 的禁止区间的贡献,并重新求解包含 \(x\) 的禁止区间。
删的话,可以记录 \(mx[i]\) 表示以 \(i\) 为左端点,最大的 \(r\)。由上面的结论, \(i \le x \le mx[i]\) 的 \(i\) 只有 \(O(\log a)\) 个。再开一棵线段树,类似上面【对于每个 \(r\) 求以其为右端点的禁止区间】的功能,暴力查找出来,一个个删掉。
重新求解上面已经说过了。
所以做到 \(O(n \log n \log a)\)。认为 \(n\) 小于值域。
这里有 9.76KByte 的超长代码。
四个不同功能(实际上可以写成三个的)的线段树,一个树状数组。
#include <bits/stdc++.h>
using namespace std;
//#define filename "xxx"
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
//#define multi_cases 1
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define ull unsigned long long
#define all(v) v.begin(), v.end()
#define upw(i, a, b) for(int i = (a); i <= (b); ++i)
#define dnw(i, a, b) for(int i = (a); i >= (b); --i)
template<class T> bool vmax(T &a, T b) { return b > a ? a = b, true : false; }
template<class T> bool vmin(T &a, T b) { return b < a ? a = b, true : false; }
template<class T> void clear(T &x) { T().swap(x); }
const int N = 1e5+5;
#define int long long
//支持区间加,区间查最小值个数
namespace SegmentTree1 {
struct data {
int mn, mnc;
data(int mn = 0, int mnc = 0) : mn(mn), mnc(mnc) { }
friend data operator + (data a, data b) { return a.mn < b.mn ? a : (a.mn > b.mn ? b : data(a.mn, a.mnc + b.mnc)); }
};
struct node {
node *l, *r;
data d;
int tag;
void up() { d = l->d + r->d; }
void upd(int v) { d.mn += v, tag += v; }
void down() { if(tag) l->upd(tag), r->upd(tag), tag = 0; }
} pool[N << 1], *tmp = pool;
node *newnode() {
tmp->l = tmp->r = NULL, tmp->d = data(0, 1), tmp->tag = 0;
return tmp++;
}
struct Sgt {
node *root;
int l, r;
void build(node *&p, int l, int r) {
p = newnode(); if(l == r) return;
int mid = l + r >> 1;
build(p->l, l, mid), build(p->r, mid+1, r), p->up();
}
void build(int l, int r) { this->l = l, this->r = r, build(root, l, r); }
void update(node *p, int l, int r, int ql, int qr, int v) {
if(l == ql && r == qr) return p->upd(v);
int mid = l + r >> 1; p->down();
if(mid >= qr) update(p->l, l, mid, ql, qr, v);
else if(mid < ql) update(p->r, mid+1, r, ql, qr, v);
else update(p->l, l, mid, ql, mid, v), update(p->r, mid+1, r, mid+1, qr, v);
p->up();
}
void update(int ql, int qr, int v) { if(ql <= qr) update(root, l, r, ql, qr, v); }
data query(node *&p, int l, int r, int ql, int qr) {
if(l == ql && r == qr) return p->d;
int mid = l + r >> 1; p->down();
if(mid >= qr) return query(p->l, l, mid, ql, qr);
if(mid < ql) return query(p->r, mid+1, r, ql, qr);
return query(p->l, l, mid, ql, mid) + query(p->r, mid+1, r, mid+1, qr);
}
int query(int ql, int qr) { data d = query(root, l, r, ql, qr); return d.mn == 0 ? d.mnc : 0; }
};
} using Sgt1 = SegmentTree1::Sgt;
//支持单点修改,区间揪出所有大于给定阈值的位置
namespace SegmentTree2 {
struct node {
node *l, *r;
int mx, tag;
void up() { mx = max(l->mx, r->mx); }
} pool[N << 1], *tmp = pool;
node *newnode() {
tmp->l = tmp->r = NULL, tmp->mx = -inf;
return tmp++;
}
struct Sgt {
node *root;
int l, r;
void build(node *&p, int l, int r) {
p = newnode(); if(l == r) return;
int mid = l + r >> 1;
build(p->l, l, mid), build(p->r, mid+1, r), p->up();
}
void build(int l, int r) { this->l = l, this->r = r, build(root, l, r); }
void update(node *p, int l, int r, int idx, int v) {
if(l == r) return p->mx = v, void();
int mid = l + r >> 1;
if(mid >= idx) update(p->l, l, mid, idx, v);
else update(p->r, mid+1, r, idx, v);
p->up();
}
void update(int idx, int v) { update(root, l, r, idx, v); }
void query(node *p, int l, int r, int ql, int qr, int v, vector<int> &pos) {
if(p->mx <= v) return;
if(l == r) return pos.push_back(l);
int mid = l + r >> 1;
if(mid >= qr) query(p->l, l, mid, ql, qr, v, pos);
else if(mid < ql) query(p->r, mid+1, r, ql, qr, v, pos);
else query(p->l, l, mid, ql, mid, v, pos), query(p->r, mid+1, r, mid+1, qr, v, pos);
}
void query(int ql, int qr, int v, vector<int> &pos) { if(ql <= qr) query(root, l, r, ql, qr, v, pos); }
};
} using Sgt2 = SegmentTree2::Sgt;
//支持区间加,区间揪出所有大于给定阈值的位置
namespace SegmentTree3 {
struct node {
node *l, *r;
int mx, tag;
void up() { mx = max(l->mx, r->mx); }
void upd(int v) { mx += v, tag += v; }
void down() { if(tag) l->upd(tag), r->upd(tag), tag = 0; }
} pool[N << 1], *tmp = pool;
node *newnode() {
tmp->l = tmp->r = NULL, tmp->mx = tmp->tag = 0;
return tmp++;
}
struct Sgt {
node *root;
int l, r;
void build(node *&p, int l, int r) {
p = newnode(); if(l == r) return;
int mid = l + r >> 1;
build(p->l, l, mid), build(p->r, mid+1, r), p->up();
}
void build(int l, int r) { this->l = l, this->r = r, build(root, l, r); }
void update(node *p, int l, int r, int ql, int qr, int v) {
if(l == ql && r == qr) return p->upd(v);
int mid = l + r >> 1; p->down();
if(mid >= qr) update(p->l, l, mid, ql, qr, v);
else if(mid < ql) update(p->r, mid+1, r, ql, qr, v);
else update(p->l, l, mid, ql, mid, v), update(p->r, mid+1, r, mid+1, qr, v);
p->up();
}
void update(int ql, int qr, int v) { if(ql <= qr) update(root, l, r, ql, qr, v); }
void query(node *p, int l, int r, int ql, int qr, int v, vector<int> &pos) {
if(p->mx <= v) return;
if(l == r) return pos.push_back(l);
int mid = l + r >> 1; p->down();
if(mid >= qr) query(p->l, l, mid, ql, qr, v, pos);
else if(mid < ql) query(p->r, mid+1, r, ql, qr, v, pos);
else query(p->l, l, mid, ql, mid, v, pos), query(p->r, mid+1, r, mid+1, qr, v, pos);
}
void query(int ql, int qr, int v, vector<int> &pos) { if(ql <= qr) query(root, l, r, ql, qr, v, pos); }
};
} using Sgt3 = SegmentTree3::Sgt;
//支持区间加,区间揪出所有小于给定阈值的位置
namespace SegmentTree4 {
struct node {
node *l, *r;
int mn, tag;
void up() { mn = min(l->mn, r->mn); }
void upd(int v) { mn += v, tag += v; }
void down() { if(tag) l->upd(tag), r->upd(tag), tag = 0; }
} pool[N << 1], *tmp = pool;
node *newnode() {
tmp->l = tmp->r = NULL, tmp->mn = tmp->tag = 0;
return tmp++;
}
struct Sgt {
node *root;
int l, r;
void build(node *&p, int l, int r) {
p = newnode(); if(l == r) return;
int mid = l + r >> 1;
build(p->l, l, mid), build(p->r, mid+1, r), p->up();
}
void build(int l, int r) { this->l = l, this->r = r, build(root, l, r); }
void update(node *p, int l, int r, int ql, int qr, int v) {
if(l == ql && r == qr) return p->upd(v);
int mid = l + r >> 1; p->down();
if(mid >= qr) update(p->l, l, mid, ql, qr, v);
else if(mid < ql) update(p->r, mid+1, r, ql, qr, v);
else update(p->l, l, mid, ql, mid, v), update(p->r, mid+1, r, mid+1, qr, v);
p->up();
}
void update(int ql, int qr, int v) { if(ql <= qr) update(root, l, r, ql, qr, v); }
void query(node *p, int l, int r, int ql, int qr, int v, vector<int> &pos) {
if(p->mn >= v) return;
if(l == r) return pos.push_back(l);
int mid = l + r >> 1; p->down();
if(mid >= qr) query(p->l, l, mid, ql, qr, v, pos);
else if(mid < ql) query(p->r, mid+1, r, ql, qr, v, pos);
else query(p->l, l, mid, ql, mid, v, pos), query(p->r, mid+1, r, mid+1, qr, v, pos);
}
void query(int ql, int qr, int v, vector<int> &pos) { if(ql <= qr) query(root, l, r, ql, qr, v, pos); }
};
} using Sgt4 = SegmentTree4::Sgt;
int n, a[N], q;
struct BIT {
vector<int> c; int n;
void init(int n) { this->n = n+5, clear(c), c.resize(n+10); }
void add(int idx, int v) { for(++idx; idx <= n; idx += idx & -idx) c[idx] += v; }
int operator [] (int idx) { int res = 0; for(++idx; idx; idx -= idx & -idx) res += c[idx]; return res; }
} sum;
Sgt1 cnttr;
Sgt2 rangetr;
Sgt4 rtr;
Sgt3 ltr;
vector<int> range[N];
int mx[N];
void calcL(int l) {
for(auto r : range[l]) cnttr.update(l+1, r-1, -1);
mx[l] = 0, clear(range[l]);
vector<int> vec;
rtr.query(l+2, n+1, sum[l], vec);
for(auto r : vec) if(a[l] > sum[r-1] - sum[l]) range[l].push_back(r);
for(auto r : range[l]) cnttr.update(l+1, r-1, 1), vmax(mx[l], r);
rangetr.update(l, mx[l]);
}
int tmp[N];
void modify(int x, int v) {
int d = v - a[x];
sum.add(x, d);
rtr.update(x, x, -d), rtr.update(x+1, n+1, d);
ltr.update(x, x, 2*d), ltr.update(x+1, n, d);
vector<int> vec;
rangetr.query(0, x, x-1, vec); //大于等于x就是大于x-1
a[x] = v;
for(auto l : vec) {
//删所有包含x的禁止区间
for(auto r : range[l]) if(r >= x) cnttr.update(l+1, r-1, -1);
sort(all(range[l])), range[l].erase(lower_bound(all(range[l]), x), range[l].end());
mx[l] = 0;
for(auto r : range[l]) vmax(mx[l], r);
rangetr.update(l, mx[l]);
}
vector<int> vl{x}, vr{x};
rtr.query(x, n+1, sum[x], vl), sort(all(vl)), vl.erase(unique(all(vl)), vl.end());
ltr.query(0, x, sum[x - 1], vr), sort(all(vr)), vr.erase(unique(all(vr)), vr.end());
for(auto l : vr) tmp[l] = sum[l];
for(auto r : vl) tmp[r-1] = sum[r-1];
for(auto l : vr) for(auto r : vl) if(l+1 < r && min(a[l], a[r]) > tmp[r-1] - tmp[l]) {
//[l, r] 为可行的包含x的禁止区间!
range[l].push_back(r);
cnttr.update(l+1, r-1, 1);
vmax(mx[l], r);
}
for(auto l : vr) rangetr.update(l, mx[l]);
}
void WaterM() {
cin >> n;
upw(i, 1, n) scanf("%lld", &a[i]);
sum.init(n), a[0] = a[n+1] = 1e18, sum.add(0, (int)1e18), sum.add(n+1, (int)1e18);
upw(i, 1, n) sum.add(i, a[i]);
cnttr.build(1, n), rtr.build(1, n+1), ltr.build(0, n);
upw(l, 0, n) ltr.update(l, l, sum[l] + a[l]);
upw(r, 1, n+1) rtr.update(r, r, sum[r-1] - a[r]);
rangetr.build(0, n);
upw(l, 0, n) calcL(l);
cin >> q;
upw(round, 1, q) {
int opt, x, y; scanf("%lld%lld%lld", &opt, &x, &y);
if(opt == 1) {
modify(x, y);
}
else {
vector<int> vec;
rangetr.query(0, x-1, y, vec);
int cnt = 0;
for(auto l : vec) for(auto r : range[l]) if(r > y) ++cnt;
cnttr.update(1, n, -cnt);
//要算提取出来的区间有没有可能多出来一段前缀禁止区间或者后缀禁止区间
//重算x-1的区间
vector<int> vl, vr;
int el = -inf, er = inf;
rtr.query(x-1 + 2, y, sum[x-1], vl);
for(auto r : vl) vmax(el, r);
cnttr.update(x, el-1, 1);
//重算y+1的区间
ltr.query(x, y+1 - 2, sum[y+1 - 1], vr);
for(auto l : vr) vmin(er, l);
cnttr.update(er+1, y, 1);
printf("%lld\n", cnttr.query(x, y));
cnttr.update(x, el-1, -1);
cnttr.update(er+1, y, -1);
cnttr.update(1, n, cnt);
}
}
}
signed main() {
#ifdef filename
FileOperations();
#endif
signed _ = 1;
#ifdef multi_cases
scanf("%d", &_);
#endif
while(_--) WaterM();
return 0;
}

浙公网安备 33010602011771号