前置芝士
- 线段树
- 平衡树/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;
}