zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Scala中的构造器和高阶函数
    Scala中的元组
    Scala中的Map
    Scala中的If判断&While&For循环
    [改善Java代码]asList方法产生的List对象不可更改
    [改善Java代码]避开基本类型数组转换列表陷阱
    [改善Java代码]枚举和注解结合使用威力更大
    [改善Java代码]枚举项的数量限制在64个以内
    [改善Java代码]用枚举实现工厂方法模式更简洁
    [改善Java代码]在switch的default代码块中增加AssertionError错误
  • 原文地址:https://www.cnblogs.com/Silymtics/p/solution-P6617.html
Copyright © 2011-2022 走看看