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

Description

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

  1. U  i  x  y\texttt{U} \; i\;x \;yUixy:将第 iii 个点修改为 (x,y)(x,y)(x,y)
  2. Q  l  r\texttt{Q} \; l \; rQlr:求 min⁡i=1n{xi−yi}(xi∈[l,r],yi∈[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]),若没有满足条件的点,答案为 000

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

Limitations

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

Solution

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

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

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

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

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

Code

3.77KB,1.3s,80.71MB  (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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值