题意:
给你一个长度为 n n n只有 1 , 2 1,2 1,2的数组和 q q q次询问,询问为两种形式:
- 1 s : 1\ s: 1 s:检查是否存在一个子数组的总和等于 s s s
- 2 i v : 2\ i\ v: 2 i v:将 a i a_i ai改为 v v v ( v i s 1 o r 2 ) (v\ is\ 1\ or\ 2) (v is 1 or 2)
思路:
既然整个数组只有1和2 那么就往边界方向去想
经过手玩可以得出以下结论:
一个序列与1连接后 这个序列可以构造出小于等于这个序列和的任何一个数
例如:
1 2 1 2 \textbf{1}\ \ \ 2\ 1\ 2 1 2 1 2就可以构造出 ( 1 + 2 + 1 + 2 = 6 ) (1+2+1+2=6) (1+2+1+2=6)以内的任何一个数
因此,我们可以记录序列中所有1的下标,并每次贪心地选取第一个1和最后一个1,并计算与其相连的序列最大和,在这里可以用树状数组或线段树维护,注意还有一种情况就是 比如第一个1与后面所有的数的和小于s,但在这个1前面还有若干个2,因此还需考虑前面若干个2的影响
Code:
//线段树维护sum
int w[N];
struct node{
int l, r, sum;
}tr[N * 4];
void build(int p, int l, int r){
tr[p] = {l, r, w[l] };
if(l == r) return ;
int m = l + r >> 1;
build(p << 1, l, m);
build(p << 1 | 1, m + 1, r);
tr[p].sum = tr[p << 1].sum + tr[p << 1 | 1].sum;
}
void update(int p, int x, int k){
if(tr[p].l == x && tr[p].r == x){
tr[p].sum += k;
return ;
}
int m = tr[p].l + tr[p].r >> 1;
if(x <= m) update(p << 1, x, k);
if(x > m) update(p << 1 | 1, x, k);
tr[p].sum = tr[p << 1].sum + tr[p << 1 | 1].sum;
}
int query(int p, int x, int y){
if(tr[p].l >= x && tr[p].r <= y){
return tr[p].sum;
}
int sum = 0;
int m = tr[p].l + tr[p].r >> 1;
if(x <= m) sum += query(p << 1, x, y);
if(y > m) sum += query(p << 1 | 1, x, y);
return sum;
}
int n, q;
cin >> n >> q;
set<int> st;
for(int i = 1; i <= n; i++){
cin >> w[i];
if(w[i] == 1){
st.insert(i);
}
}
build(1, 1, n);
while(q --){
int op, s, i, v;
cin >> op;
if(op == 1){
cin >> s;
if(!st.size()){
//若序列中没有1存在
if(s & 1){
cout << "NO" << endl;
}else{
cout << "YES" << endl;
}
}else{
//第一个1的下标
auto idx = *st.begin();
//第二个1的下标
auto idx2 = *st.rbegin();
if((s <= query(1, idx, n)) || ((s - query(1, idx, n)) % 2 == 0 && (s - query(1, idx, n)) / 2 <= idx - 1) ||
(s <= query(1, 1, idx2)) ||((s - query(1, 1, idx2) % 2 == 0 && (s - query(1, 1, idx2)) / 2 <= n - idx2)) ){
cout << "YES" << endl;
}else{
cout << "NO" << endl;
}
}
}else{
cin >> i >> v;
if(w[i] == v){
continue;
}else{
if(w[i] == 1){
w[i] = 2;
update(1, i, 1);
st.erase(i);
}else{
w[i] = 1;
update(1, i, -1);
st.insert(i);
}
}
}
}