题解:P11695 [JRKSJ ExR] 昼寝
KingPowers · · 题解
这道题太深刻了,我必须要记录一下。
首先为了方便,把所有的区间都变成闭区间。用
对询问做猫树分治,假设现在的分治区间是
先考虑处理跨过
再考虑使用两边的区间能不能填满剩下的部分。同样以左边为例,我们希望对每个询问求出从左端点开始向右最远能填到哪。现在考虑对序列从左往右扫描线,使用数据结构维护每个询问向后填线段的过程。具体来说以时间为下标建线段树,维护每个时间的询问当前向右延申到哪,扫到一个询问就加入线段树并初始化成
右边也是同理的,求出这些信息后容易判断一个询问的答案。每个操作区间会在分治树上的
代码马上就写。upd:写完了。
//大分治题,真牛
#include <bits/stdc++.h>
using namespace std;
//#define int long long
#define For(i, a, b) for(int i = (a); i <= (b); i++)
#define Rof(i, a, b) for(int i = (a); i >= (b); i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__)
#define range(Tx) begin(Tx), end(Tx)
using pr = array<int, 2>;
template<typename T>void cmax(T &x, T y){x = max(x, y);}
template<typename T>void cmin(T &x, T y){x = min(x, y);}
const int N = 1e6 + 5, inf = 1e9;
int n, m, q, tag[N], ans[N];
int tim[N], covl[N], covr[N], tol[N], tor[N];
multiset<int> sl[N], sr[N];
struct oper{int l, r, tl, tr;}a[N];
struct que{int l, r, t, id;}b[N];
vector<oper> chg[N], ins[N], del[N]; vector<que> qry[N];
#define ls now << 1
#define rs now << 1 | 1
struct SegTree1{
int mn[N << 2];
void pushup(int now){mn[now] = min(mn[ls], mn[rs]);}
void build(int l, int r, int now){
mn[now] = inf;
if(l == r) return;
int mid = (l + r) >> 1;
build(l, mid, ls); build(mid + 1, r, rs);
}
void update(int p, int l, int r, int now){
if(l == r) return mn[now] = sl[l].empty() ? inf : *sl[l].begin(), void();
int mid = (l + r) >> 1;
if(p <= mid) update(p, l, mid, ls);
else update(p, mid + 1, r, rs);
pushup(now);
}
int find(int x, int y, int v, int l, int r, int now){
if(mn[now] > v) return -1;
if(l == r) return l;
int mid = (l + r) >> 1, res = -1;
if(x <= mid && (res = find(x, y, v, l, mid, ls)) != -1) return res;
return find(x, y, v, mid + 1, r, rs);
}
}T1;
struct SegTree2{
int mx[N << 2];
void pushup(int now){mx[now] = max(mx[ls], mx[rs]);}
void build(int l, int r, int now){
mx[now] = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(l, mid, ls); build(mid + 1, r, rs);
}
void update(int p, int l, int r, int now){
if(l == r) return mx[now] = sr[r].empty() ? 0 : *sr[r].rbegin(), void();
int mid = (l + r) >> 1;
if(p <= mid) update(p, l, mid, ls);
else update(p, mid + 1, r, rs);
pushup(now);
}
int find(int x, int y, int v, int l, int r, int now){
if(mx[now] < v) return -1;
if(l == r) return l;
int mid = (l + r) >> 1, res = -1;
if(y > mid && (res = find(x, y, v, mid + 1, r, rs)) != -1) return res;
return find(x, y, v, l, mid, ls);
}
}T2;
struct SegTree3{
pr mn[N << 2]; int tag[N << 2];
void pushup(int now){mn[now] = min(mn[ls], mn[rs]);}
void pusht(int now, int v){cmax(mn[now][0], v); cmax(tag[now], v);}
void pushdown(int now){
if(!tag[now]) return;
pusht(ls, tag[now]); pusht(rs, tag[now]);
tag[now] = 0;
}
void build(int l, int r, int now){
mn[now] = {inf, 0}; tag[now] = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(l, mid, ls); build(mid + 1, r, rs);
}
void modify(int x, int y, int v, int l, int r, int now){
if(x <= l && r <= y) return pusht(now, v);
int mid = (l + r) >> 1; pushdown(now);
if(x <= mid) modify(x, y, v, l, mid, ls);
if(y > mid) modify(x, y, v, mid + 1, r, rs);
pushup(now);
}
void update(int p, pr v, int l, int r, int now){
if(l == r) return mn[now] = v, void();
int mid = (l + r) >> 1; pushdown(now);
if(p <= mid) update(p, v, l, mid, ls);
else update(p, v, mid + 1, r, rs);
pushup(now);
}
}T3;
struct SegTree4{
pr mx[N << 2]; int tag[N << 2];
void pushup(int now){mx[now] = max(mx[ls], mx[rs]);}
void pusht(int now, int v){cmin(mx[now][0], v); cmin(tag[now], v);}
void pushdown(int now){
if(tag[now] == inf) return;
pusht(ls, tag[now]); pusht(rs, tag[now]);
tag[now] = inf;
}
void build(int l, int r, int now){
mx[now] = {-inf, 0}; tag[now] = inf;
if(l == r) return;
int mid = (l + r) >> 1;
build(l, mid, ls); build(mid + 1, r, rs);
}
void modify(int x, int y, int v, int l, int r, int now){
if(x <= l && r <= y) return pusht(now, v);
int mid = (l + r) >> 1; pushdown(now);
if(x <= mid) modify(x, y, v, l, mid, ls);
if(y > mid) modify(x, y, v, mid + 1, r, rs);
pushup(now);
}
void update(int p, pr v, int l, int r, int now){
if(l == r) return mx[now] = v, void();
int mid = (l + r) >> 1; pushdown(now);
if(p <= mid) update(p, v, l, mid, ls);
else update(p, v, mid + 1, r, rs);
pushup(now);
}
}T4;
#undef ls
#undef rs
void solve(int l, int r, vector<oper> vop, vector<que> vqry){
if(l >= r || vop.empty() || vqry.empty()) return;
int mid = (l + r) >> 1;
//分类询问,对时间离散化
vector<oper> opl, opr, opm;
vector<que> qryl, qryr, qrym;
vector<int> num;
for(auto t : vop){
if(t.r <= mid) opl.push_back(t);
else if(t.l > mid) opr.push_back(t);
else opm.push_back(t);
}
for(auto t : vqry){
if(t.r <= mid) qryl.push_back(t);
else if(t.l > mid) qryr.push_back(t);
else qrym.push_back(t);
}
for(auto t : vop) num.push_back(t.tl), num.push_back(t.tr);
for(auto t : qrym) num.push_back(t.t);
sort(range(num)); num.erase(unique(range(num)), num.end());
auto F = [&](int x){return lower_bound(range(num), x) - num.begin() + 1;};
vector<oper> dwnl = opl, dwnr = opr;
for(auto &t : opl) t.tl = F(t.tl), t.tr = F(t.tr);
for(auto &t : opm) t.tl = F(t.tl), t.tr = F(t.tr);
for(auto &t : opr) t.tl = F(t.tl), t.tr = F(t.tr);
for(auto &t : qrym) tim[t.id] = t.t = F(t.t);
int tot = num.size();
//从 mid 开始,向两边覆盖
for(auto t : opm) ins[t.tl].push_back(t), del[t.tr].push_back(t);
for(auto t : qrym) qry[t.t].push_back(t);
T1.build(l, r, 1); T2.build(l, r, 1);
For(i, 1, tot){
for(auto t : ins[i]){
sl[t.l].insert(t.r), T1.update(t.l, l, r, 1);
sr[t.r].insert(t.l); T2.update(t.r, l, r, 1);
}
for(auto t : qry[i]){
int res = T1.find(t.l, mid, t.r, l, r, 1);
covl[t.id] = res == -1 ? mid + 1 : res;
res = T2.find(mid + 1, t.r, t.l, l, r, 1);
covr[t.id] = res == -1 ? mid : res;
}
for(auto t : del[i]){
sl[t.l].erase(sl[t.l].find(t.r)); T1.update(t.l, l, r, 1);
sr[t.r].erase(sr[t.r].find(t.l)); T2.update(t.r, l, r, 1);
}
}
For(i, 1, tot) ins[i].clear(), del[i].clear(), qry[i].clear();
For(i, l, r) sl[i].clear(), sr[i].clear();
//左区间覆盖
for(auto t : opl) chg[t.l].push_back(t);
for(auto t : qrym) qry[t.l].push_back(t), tor[t.id] = mid + 1;
T3.build(1, tot, 1);
For(i, l, mid){
for(auto t : qry[i]) T3.update(t.t, {i, t.id}, 1, tot, 1);
for(auto t : chg[i]) T3.modify(t.tl, t.tr, t.r + 1, 1, tot, 1);
while(T3.mn[1][0] == i){
int p = T3.mn[1][1]; tor[p] = i;
T3.update(tim[p], {inf, 0}, 1, tot, 1);
}
}
For(i, l, r) chg[i].clear(), qry[i].clear();
//右区间覆盖
for(auto t : opr) chg[t.r].push_back(t);
for(auto t : qrym) qry[t.r].push_back(t), tol[t.id] = mid;
T4.build(1, tot, 1);
Rof(i, r, mid + 1){
for(auto t : qry[i]) T4.update(t.t, {i, t.id}, 1, tot, 1);
for(auto t : chg[i]) T4.modify(t.tl, t.tr, t.l - 1, 1, tot, 1);
while(T4.mx[1][0] == i){
int p = T4.mx[1][1]; tol[p] = i;
T4.update(tim[p], {0, 0}, 1, tot, 1);
}
}
For(i, l, r) chg[i].clear(), qry[i].clear();
//统计答案,递归
for(auto t : qrym) ans[t.id] = (tor[t.id] >= covl[t.id]) && (tol[t.id] <= covr[t.id]);
solve(l, mid, dwnl, qryl); solve(mid + 1, r, dwnr, qryr);
}
void Solve(){
cin >> n >> m;
vector<int> v1, v2;
For(i, 1, m){
int op, t; cin >> op;
if(op == 1){
cin >> a[i].l >> a[i].r; --a[i].r;
a[i].tl = i; a[i].tr = m; v1.push_back(i);
if(a[i].l == a[i].r) tag[a[i].l]++;
}
else if(op == 2){
cin >> t, a[t].tr = i - 1;
if(a[t].l == a[t].r) tag[a[t].l]--;
}
else{
cin >> b[i].l >> b[i].r; --b[i].r;
b[i].id = ++q; b[i].t = i; v2.push_back(i);
if(b[i].l == b[i].r) ans[q] = !!tag[b[i].l];
}
}
vector<oper> all1; vector<que> all2;
for(int i : v1) all1.push_back(a[i]);
for(int i : v2) all2.push_back(b[i]);
solve(1, n - 1, all1, all2);
For(i, 1, q) cout << (ans[i] ? 'Y' : 'N') << '\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
int T = 1; //cin >> T;
while(T--) Solve();
return 0;
}
如你所见,虽然我写了整整 7.6k,但是四棵线段树里前两棵和后两棵功能基本完全一致,而且剩下的地方也没什么繁琐的细节,所以其实好写好调。