icpc2019 香港 H. Hold the Line (离线+线段树+单调队列)
题目链接:https://codeforces.com/gym/102452/problem/H
将所有询问离线下来,按照右端点从小到大排序,每处理一个新询问,就将所有位置小于等于 \(r\) 的士兵加入线段树,线段树每个节点维护的高度在 \([l,r]\) 内的所有节点(位置和插入时间),那么对于当前询问,线段树中所有的点都是小于 \(r\) 的,我们要找到位置大于 \(l\) 并且插入时间在该询问之前的节点。
如果线段树同一个节点上,有士兵位置更靠左(\(x\) 较小)且插入时间更晚,那我们不需要保留该士兵,于是我们维护的就是位置和时间都递增的单调队列,查询是否有合法节点,在该节点的单调队列中二分查找即可。
时间复杂度 \(O(nlogn + mlog^2n)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000010;
const int INF = 1000000007;
int T;
int n, m;
int tot, b[maxn], ans[maxn];
int c;
struct CH{
int x, h, t;
bool operator < (const CH &y) const{
return x < y.x;
}
}cha[maxn];
int q;
struct Q{
int id;
int l, r, h, t;
bool operator < (const Q &y) const{
return r < y.r;
}
}que[maxn];
struct Node{
int tail = 0;
vector<int> q;
vector<int> ti;
}t[maxn<<2];
void build(int i, int l, int r){
t[i].tail = 0; t[i].q.clear();
t[i].ti.clear();
if(l == r){
return;
}
int mid = (l+r) >> 1;
build(i<<1, l, mid); build(i<<1|1, mid+1, r);
}
void mdf(int i, int p, int lp, int ti, int l, int r){
while(t[i].tail > 0 && t[i].ti[t[i].tail-1] > ti) {
--t[i].tail;
}
if(t[i].q.size() == t[i].tail) {
t[i].q.push_back(lp);
t[i].ti.push_back(ti);
t[i].tail++;
} else{
t[i].q[t[i].tail] = lp;
t[i].ti[t[i].tail] = ti;
t[i].tail++;
}
if(l == r){
return;
}
int mid = (l+r) >> 1;
if(p <= mid) mdf(i<<1, p, lp, ti, l, mid);
else mdf(i<<1|1, p, lp, ti, mid+1, r);
}
bool check(int i, int l, int r, int pos, int ti){
int L = 0, R = t[i].tail-1;
while(L <= R){
int mid = (L + R) >> 1;
if(t[i].q[mid] >= pos && t[i].ti[mid] <= ti) return true;
if(t[i].q[mid] < pos){
L = mid + 1;
} else{
R = mid - 1;
}
}
return false;
}
int qry_l(int i, int l, int r, int pos, int ti, int hei){
if(l == r){
return abs(b[hei]-b[l]);
}
int mid = (l+r) >> 1;
int res = INF;
if(hei > mid && check(i<<1|1, mid+1, r, pos, ti)) res = qry_l(i<<1|1, mid+1, r, pos, ti, hei);
if(res != INF) return res;
if(check(i<<1, l, mid, pos, ti)) res = qry_l(i<<1, l, mid, pos, ti, hei);
return res;
}
int qry_r(int i, int l, int r, int pos, int ti, int hei){
if(l == r){
return abs(b[hei]-b[l]);
}
int mid = (l+r) >> 1;
int res = INF;
if(hei <= mid && check(i<<1, l, mid, pos, ti)) res = qry_r(i<<1, l, mid, pos, ti, hei);
if(res != INF) return res;
if(check(i<<1|1, mid+1, r, pos, ti)) res = qry_r(i<<1|1, mid+1, r, pos, ti, hei);
return res;
// if(hei <= mid && check(i<<1, l, mid, pos, ti)) return qry_r(i<<1, l, mid, pos, ti, hei);
// else if(check(i<<1|1, mid+1, r, pos, ti)) return qry_r(i<<1|1, mid+1, r, pos, ti, hei);
// else return INF;
}
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
T = read();
while(T--){
tot = 0, c = 0, q = 0;
n = read(), m = read();
int op, x, h, l, r;
for(int i = 1 ; i <= m ; ++i){
op = read();
if(op == 0){
x = read(), h = read();
cha[++c].x = x, cha[c].h = h, cha[c].t = i;
b[++tot] = h;
} else{
l = read(), r = read(), h = read();
++q;
que[q].id = q, que[q].l = l, que[q].r = r, que[q].h = h, que[q].t = i;
b[++tot] = h;
}
}
sort(cha+1, cha+1+c);
sort(que+1, que+1+q);
sort(b+1, b+1+tot);
tot = unique(b+1, b+1+tot)-b-1;
for(int i = 1 ; i <= c ; ++i) cha[i].h = lower_bound(b+1, b+1+tot, cha[i].h) - b;
for(int i = 1 ; i <= q ; ++i) que[i].h = lower_bound(b+1, b+1+tot, que[i].h) - b;
build(1, 1, tot); // may be tle
int cur = 0;
for(int i = 1 ; i <= q ; ++i){
while(cur < c && cha[cur+1].x <= que[i].r) {
mdf(1, cha[cur+1].h, cha[cur+1].x, cha[cur+1].t, 1, tot); //insert the r-th soilder
++cur;
}
if(!check(1, 1, tot, que[i].l, que[i].t)) ans[que[i].id] = -1;
else {
int res = INF;
res = min(res, qry_l(1, 1, tot, que[i].l, que[i].t, que[i].h));
res = min(res, qry_r(1, 1, tot, que[i].l, que[i].t, que[i].h));
ans[que[i].id] = res; // find the ans
}
}
for(int i = 1 ; i <= q ; ++i) printf("%d\n", ans[i]);
}
return 0;
}