P11210 『STA - R8』强制在线动态二维数点

Description

给定 n n n 个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x n , y n ) (x_1,y_1),(x_2,y_2),\cdots,(x_n,y_n) (x1,y1),(x2,y2),,(xn,yn),进行 m m m 次操作,有以下两种:

  1. U    i    x    y \texttt{U} \; i\;x \;y Uixy:将第 i i i 个点修改为 ( x , y ) (x,y) (x,y)
  2. Q    l    r \texttt{Q} \; l \; r Qlr:求 min ⁡ i = 1 n { x i − y i } ( x i ∈ [ l , r ] , y i ∈ [ l , r ] ) \min_{i=1}^{n} \{x_i-y_i \} \quad (x_i\in[l,r],y_i\in[l,r]) mini=1n{xiyi}(xi[l,r],yi[l,r]),若没有满足条件的点,答案为 0 0 0

强制在线,每次操作的参数需要异或上一次的答案,如没有则不需要。

Limitations

1 ≤ n , m ≤ 5 × 1 0 5 1 \le n,m \le 5\times 10^5 1n,m5×105
1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1lrn实际
1 ≤ y ≤ x ≤ n \textcolor{red}{1 \le y \le x \le n} 1yxn实际
2 s , 512 MB 2\texttt{s},512\texttt{MB} 2s,512MB

Solution

注意到 y ≤ x y \le x yx,可以把问题转化为:给定 n n n 个区间,支持修改某个区间,查询包含于 [ l , r ] [l,r] [l,r] 的区间的长度的最小值。

由于能修改而且强制在线,我们不能直接数点。
但我们可以排除包含了其他区间的区间,显然它不可能成为答案。

此时左右端点均单调不降,只需要找出第一个 ≥ l \ge l l l p l_p lp 和最后一个 ≤ r \le r r r q r_q rq,答案就是 min ⁡ i = p q { r i − l i } \min_{i=p}^q \{r_i-l_i\} mini=pq{rili}

实现上,无需删掉没用的区间,只需将区间按右端点分开维护,只管一个右端点对应的最大左端点即可,顺便解决了 q q q

使用 multiset 和线段树维护, p p p 可以在线段树上二分求得,时间复杂度 O ( ( n + q ) log ⁡ n ) \mathcal{O}((n+q)\log n) O((n+q)logn)

Code

3.77 KB , 1.3 s , 80.71 MB    (in total) 3.77\texttt{KB},1.3\texttt{s},80.71\texttt{MB} \; \text{(in total)} 3.77KB,1.3s,80.71MB(in total)

// Problem: P11210 『STA - R8』强制在线动态二维数点
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P11210
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

int ls(int u) { return u * 2 + 1; }
int rs(int u) { return u * 2 + 2; }

struct Node {
    int l, r, mx, mi;
};
const int inf = 1e9;
using Tree = vector<Node>;

void pushup(Tree &tr, int u) {
    tr[u].mx = max(tr[ls(u)].mx, tr[rs(u)].mx);
    tr[u].mi = min(tr[ls(u)].mi, tr[rs(u)].mi);
}

void build(Tree &tr, int u, int l, int r, vector<multiset<int>> &S) {
    tr[u].l = l;
    tr[u].r = r;
    
    if (l == r) {
        if (S[l].empty()) {
            tr[u].mx = 0;
            tr[u].mi = inf;
        }
        else {
            tr[u].mx = *prev(S[l].end());
            tr[u].mi = l - tr[u].mx;
        }
        return;
    }
    
    int mid = (l + r) >> 1;
    build(tr, ls(u), l, mid, S);
    build(tr, rs(u), mid + 1, r, S);
    pushup(tr, u);
}

void modify(Tree &tr, int u, int p, vector<multiset<int>> &S) {
    if (tr[u].l == tr[u].r) {
        if (S[tr[u].l].empty()) {
            tr[u].mx = 0;
            tr[u].mi = inf;
        }
        else {
            tr[u].mx = *prev(S[tr[u].l].end());
            tr[u].mi = tr[u].l - tr[u].mx;
        }
        return;
    }
    
    int mid = (tr[u].l + tr[u].r) >> 1;
    if (p <= mid) {
        modify(tr, ls(u), p, S);
    }
    else {
        modify(tr, rs(u), p, S);
    }
    pushup(tr, u);
}

int query(Tree &tr, int u, int l, int r) {
    if (l <= tr[u].l && tr[u].r <= r) {
        return tr[u].mi;
    }
    
    int mid = (tr[u].l + tr[u].r) >> 1;
    int res = inf;
    if (l <= mid) {
        res = min(res, query(tr, ls(u), l, r));
    }
    if (r > mid) {
        res = min(res, query(tr, rs(u), l, r));
    }
    return res;
}

int find_first(Tree &tr, int u, int l, int r, int v) {
    if (tr[u].r < l || tr[u].l > r || tr[u].mx < v) {
        return -1;
    }
    if (tr[u].l == tr[u].r) {
        return tr[u].l;
    }
    
    int res = find_first(tr, ls(u), l, r, v);
    if (res == -1) {
        res = find_first(tr, rs(u), l, r, v);
    }
    return res;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, q;
	cin >> n >> q;
	
	vector<multiset<int>> S(n);
	vector<int> x(n), y(n);
	
	for (int i = 0; i < n; i++) {
	    cin >> x[i] >> y[i];
	    x[i]--, y[i]--;
	    S[x[i]].insert(y[i]);
	}
	
	Tree tr(n << 2);
	build(tr, 0, 0, n - 1, S);
	
	auto update = [&](int i, int a, int b) {
	    S[x[i]].extract(y[i]);
	    modify(tr, 0, x[i], S);
	    
	    x[i] = a;
	    y[i] = b;
	    S[x[i]].insert(y[i]);
	    
	    modify(tr, 0, x[i], S);
	    
	};
	
	auto get = [&](int l, int r) {
	    int fir = find_first(tr, 0, l, r, l);
	    if (fir == -1) {
	        return 0;
	    }
	    return query(tr, 0, fir, r);
	};
	
	int ans = 0;
	for (int i = 0; i < q; i++) {
	    char op;
	    cin >> op;
	    if (op == 'U') {
	        int i, a, b;
	        cin >> i >> a >> b;
	        i ^= ans, a ^= ans, b ^= ans;
	        i--, a--, b--;
	        update(i, a, b);
	    }
	    else {
	        int l, r;
	        cin >> l >> r;
	        l ^= ans, r ^= ans;
	        l--, r--;
	        cout << (ans = get(l, r)) << endl;
	    }
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值