LG 题解 P6617 查找 Search

前置芝士

  • 线段树
  • 平衡树/STL-set

Description

给你一个长为 (n) 序列 (a),一个标准 (w),有 (m) 次询问,分两种情况:

  • 修改一个元素的值
  • 询问一段区间 ([l,r]) 内有没有一对元素 ((x,y)) 满足 (a_x + a_y = w)

Solution

我们对每一个元素 (x),设 (nxt_x) 表示 (x) 往后第一个与它相加为 (w) 的元素的位置,即 (a_x + a_{nxt_x} = w)

那么,如果 (min_{i=l}^{r} nxt_i le r),说明有解,否则无解。

考虑修改操作,(x) 可以作为 (1 sim x-1) 中任何一个数的 (nxt)(nxt_x) 也可能可以指向多个位置,这样做会让复杂度爆炸。

我们重新定义 (nxt_x),让其表示 (x) 往后第一个与它相加为 (w) 的元素并且两个元素之间没有与 (a_x) 相同的元素。

举个例子,如果一个序列是这样:

[[x,x,w-x] ]

那么它对应的 (nxt) 是这样(我们将没有匹配的设为 (n+1)):

[[4,3,4] ]

只有第二个 (x)(nxt) 指向 (w-x)

我们发现这样仍然满足正确性,并且减少了许多冗余的信息。

这样把 (x) 修改成 (y) 之后牵扯到的点最多有四个,上一个 (x),上一个 (w-x),这个的新的 (y)(nxt),上一个 (y)(nxt)

(x)(w-x) 放进同一个 set 里就可以快速查询它们的前驱后继了。

Code

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 5e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

int n, m, W, cnt = 0;
int nxt[MAXN], a[MAXN];
set<int> s[MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

namespace Seg {
    #define lson i << 1
    #define rson i << 1 | 1
    int Min[MAXN << 2];
    void Push_up(int i) { Min[i] = min(Min[lson], Min[rson]); }
    void Build(int i, int l, int r) {
        if(l == r) { Min[i] = nxt[l]; return ; }
        int mid = (l + r) >> 1;
        Build(lson, l, mid), Build(rson, mid + 1, r);
        Push_up(i);
    }
    void Modify(int i, int l, int r, int pos, int val) {
        if(l == r) { Min[i] = val; return ; }
        int mid = (l + r) >> 1;
        if(mid >= pos) Modify(lson, l, mid, pos, val);
        else Modify(rson, mid + 1, r, pos, val);
        Push_up(i);
    }
    int Get_Min(int i, int l, int r, int L, int R) {
        if(L <= l && r <= R) return Min[i];
        int mid = (l + r) >> 1, ans = INF;
        if(mid >= L) ans = min(ans, Get_Min(lson, l, mid, L, R));
        if(mid < R) ans = min(ans, Get_Min(rson, mid + 1, r, L, R));
        return ans;
    }
}

int main()
{
    n = read(), m = read(), W = read();
    for(int i = 1; i <= n; ++i) {
        a[i] = read();
        s[min(a[i], W - a[i])].insert(i);
    }
    for(int i = 0; i <= W / 2; ++i) s[i].insert(n + 1);
    for(int i = 1; i <= n; ++i) {
        set<int>::iterator it = s[min(a[i], W - a[i])].upper_bound(i);
        nxt[i] = (a[*it] + a[i] == W ? *it : n + 1);
    }
    Seg::Build(1, 1, n);
    for(int i = 1, opt, x, y; i <= m; ++i) {
        opt = read(), x = read(), y = read();
        if(opt == 1) {
            // 因为这里我们把 v 和 W - v 维护在一个 set 里面,v 的后继可以是与它自己值相同的位置,不影响正确性,并方便修改。 
            s[min(a[x], W - a[x])].erase(x); // 删除这个值在 set 中的位置 
            set<int>::iterator it = s[min(a[x], W - a[x])].lower_bound(x); 
            if(it != s[min(a[x], W - a[x])].begin()) { // 修改前驱 
                int lst = *it; it--;
                nxt[*it] = (a[lst] + a[*it] == W ? lst : n + 1);
                Seg::Modify(1, 1, n, *it, nxt[*it]);
            }
            
            a[x] = y; // 修改这个值 
            s[min(y, W - y)].insert(x); // 插入 
            it = s[min(y, W - y)].upper_bound(x); 
            nxt[x] = (a[*it] + a[x] == W ? *it : n + 1); // 修改后继 
            Seg::Modify(1, 1, n, x, nxt[x]);
            
            it = s[min(y, W - y)].lower_bound(x); 
            if(it != s[x].begin()) { // 修改新的前驱 
                int tmp = *it; it--;
                nxt[*it] = a[tmp] + a[*it] == W ? tmp : n + 1;
                Seg::Modify(1, 1, n, *it, nxt[*it]);
            }
        } else {
            x ^= cnt, y ^= cnt;
            if(Seg::Get_Min(1, 1, n, x, y) <= y) cnt++, puts("Yes");
            else puts("No");
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Silymtics/p/solution-P6617.html