给定长度为字符串(下标范围为),执行两个操作:
  1. 区间取反:将这个区间中的全部元素进行取反操作,
  2.  区间数一:输出下标在这个区间中的所有元素值为1的元素的个数,即
前面有一道简单的题目是相同的区间取操作+单点查询的值为还是,解决的办法是维护区间的取反次数,如果为奇数,那么输出,否则输出
但是这里区间查询这个区间中的1的的个数,但是是一模一样的,没有任何区别
结果就是我以为线段树不能解决,然后想到用分块来做:
分块+二分优化:
#include<bits/stdc++.h>
using namespace std;

#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define HelloWorld IOS;


const int N = 5e5 + 10;
int n, q, block_size;

vector<int> block[N];
int a[N], cnt[N];

void reset_block(int idx){
    block[idx].clear();
    int l = (idx - 1) * block_size + 1;
    int r = min(idx * block_size, n);
    for(int i = l; i <= r; i ++) block[idx].push_back(a[i]);
    sort(block[idx].begin(), block[idx].end());
}

void block_cnt(int l, int r){
    int idx_l = (l - 1) / block_size + 1;
    int idx_r = (r - 1) / block_size + 1;
    if(idx_l == idx_r){
        for(int i = l; i <= r; i ++) a[i] = 1 - a[i];
        reset_block(idx_l);
    }
    else{
        int left_end = idx_l * block_size;
        for(int i = l; i <= left_end; i ++) a[i] = 1 - a[i];
        reset_block(idx_l);
        for(int i = idx_l + 1; i <= idx_r - 1; i ++) cnt[i] += 1;
        int right_begin = (idx_r - 1) * block_size + 1;
        for(int i = right_begin; i <= r; i ++) a[i] = 1 - a[i];
        reset_block(idx_r); 
    }
}

int query(int l, int r){
    int res = 0;
    int idx_l = (l - 1) / block_size + 1;
    int idx_r = (r - 1) / block_size + 1;
    if(idx_l == idx_r){
        for(int i = l; i <= r; i ++){
            if(a[i] == 1 && !(cnt[idx_l] & 1)) res ++;
            else if(a[i] == 0 && cnt[idx_l] & 1) res ++;
        }
    }
    else{
        int left_end = idx_l * block_size;
        for(int i = l; i <= left_end; i ++){
            if(a[i] == 0 && cnt[idx_l] & 1) res ++;
            else if(a[i] == 1 && !(cnt[idx_l] & 1)) res ++;
        }
        for(int i = idx_l + 1; i <= idx_r - 1; i ++){
            auto it = lower_bound(block[i].begin(), block[i].end(), 1);
            if(it == block[i].begin()){
                if(!(cnt[i] & 1)) res += block_size;
            }
            else if(it == block[i].end()){
                if(cnt[i] & 1) res += block_size;
            }
            else{
               // it --;
                int pos = it - block[i].begin();
                if(cnt[i] & 1) res += pos;
                else res += block_size - pos;
            }
        }
        int right_begin = (idx_r - 1) * block_size + 1;
        for(int i = right_begin; i <= r; i ++){
            if(a[i] == 0 && cnt[idx_r] & 1) res ++;
            else if(a[i] == 1 && !(cnt[idx_r] & 1)) res ++;
        }
    }
    return res;
}
signed main(){
    HelloWorld;
    
    cin >> n >> q;
    for(int i = 1; i <= n; i ++){
        char sh; cin >> sh;
        a[i] = sh - '0';
    }
    block_size = (int)sqrt(n) + 1;
    int total_block = (n - 1) / block_size + 1;
    for(int i = 1; i <= total_block; i ++) reset_block(i);

    while(q --){
        int op; cin >> op;
        if(op == 1){
            int l, r; cin >> l >> r;
            block_cnt(l, r);
        }
        else{
            int l, r; cin >> l >> r;
            cout << query(l, r) << endl;
        }
    }
    return 0;
}
结果超时了,时间复杂度一般为取最大时,,但实际数据可能会更大,超时时必然的
如果取最大时,则,则不会超时
正确的线段树做法:
总代码:
#include<bits/stdc++.h>
using namespace std;

#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define HelloWorld IOS;

const int N = 5e5 + 10;

struct Node{
    int l, r;
    int sum, cnt;
}tr[4 * N];

int n, q, a[N];

void push_up(int u){
    tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum);
}

void push_down(int u){
    if(tr[u].cnt & 1){
        tr[u << 1].cnt += 1;
        tr[u << 1].sum = (tr[u << 1].r - tr[u << 1].l + 1) - tr[u << 1].sum;
        tr[u << 1 | 1].cnt += 1;
        tr[u << 1 | 1].sum = (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) - tr[u << 1 | 1].sum;
        tr[u].cnt += 1;
    }
}

void build(int u, int l, int r){
    if(l == r) tr[u] = {l, r, a[l], 0};
    else{
        tr[u] = {l, r, 0, 0};
        int mid = (tr[u].l + tr[u].r) >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        push_up(u);
    }
}

void modify(int u, int l, int r){
    if(l <= tr[u].l && tr[u].r <= r){
        tr[u].sum = (tr[u].r - tr[u].l + 1) - tr[u].sum;
        tr[u].cnt += 1;
        return ;
    }
    else{
        push_down(u);
        int mid = (tr[u].l + tr[u].r) >> 1;
        if(l <= mid) modify(u << 1, l, r);
        if(r > mid) modify(u << 1 | 1, l, r);
        push_up(u);
    }
}

int query(int u, int l, int r){
    if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
    else{
        int res = 0;
        push_down(u);
        int mid = (tr[u].l + tr[u].r) >> 1;
        if(l <= mid) res += query(u << 1, l, r);
        if(r > mid) res += query(u << 1 | 1, l, r);
        return res;
    }
}

signed main(){
    HelloWorld;
    
    cin >> n >> q;
    for(int i = 1; i <= n; i ++){
        char sh; cin >> sh;
        a[i] = sh - '0';
    }
    build(1, 1, n);

    while(q --){
        int op; cin >> op;
        if(op == 1){
            int l, r; cin >> l >> r;
            modify(1, l, r);
        }
        else{
            int l, r; cin >> l >> r;
            cout << query(1, l, r) << endl;
        }
    }
    return 0;
}