CodeTON Round 7 (Div. 1 + Div. 2, Rated, Prizes!) D. Ones and Twos

题意:

给你一个长度为 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);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值