萌新刚学OI,分块10分求助
查看原帖
萌新刚学OI,分块10分求助
262147
Reobrok_Kk楼主2021/3/31 15:14
#include <bits/stdc++.h>
using namespace std;
int len, n, a[100005], b[100005], m;
int id[100005], sum[100005];
void change(int l, int r) {
    if (id[l] == id[r]) {
        for (int i = l; i <= r; i++) {
            a[i] = (a[i] + 1) % 2; //0或1加1再对2取膜等价于反转
            sum[id[i]] += (a[i] == 1 ? 1 : -1); //a[i]为一说明原来是0, 加了1, 为0则相反
        }
    }
    else {
        for (int i = l; id[i] == id[l]; i++) {
            a[i] = (a[i] + 1) % 2;
            sum[id[i]] += (a[i] == 1 ? 1 : -1);
            //左半块反转
        }
        for (int i = id[l] + 1; i < id[r]; i++) {
            b[i] = (b[i] + 1) %2; //加2相当于没变, 加3相当于加1, 所以块被加和也膜2
            sum[i] += len - sum[i]; //块和反转, 如10010, 反转后为01101, 原有2个, 现有3个, 可见块和反转直接用块长-原块和
        }
        for (int i = r; id[i] == id[r]; i--) {
            a[i] = (a[i] + 1) %2;
            sum[id[i]] += (a[i] == 1 ? 1 : -1);
            //右半块反转
        }
    }
    return ;
}
int query(int l, int r) {
    int ans = 0;
    if (id[l] == id[r]) {
        for (int i = l; i <= r; i++) {
            ans += (a[i] + b[id[i]]) % 2; //计算i点是否打开
        }
    }
    else {
        for (int i = l; id[i] == id[l]; i++)
            ans += (a[i] + b[id[i]]) %2;
        //左半块相加
        for (int i = id[l] + 1; i < id[r]; i++)
            ans += sum[i]; //块和已经处理好了, 直接加上
        for (int i = r; id[i] == id[r]; i--)
            ans += (a[i] + b[id[i]]) % 2;
        //右半块相加
    }
    return ans;
}
int main() {
    cin >> n >> m;
    len = sqrt(n); //求块长
    for (int i = 1; i <= n; i++)
        id[i] = i / len + 1; //分块
    for (int i = 1, k, l, r; i <= m; i++) {
        cin >> k >> l >> r;
        switch(k) {
        case 0:
            change(l, r); //区间反转
            break;
        case 1:
            cout << query(l, r) << endl; //区间查询
            break;
        }
    }
    return 0;
}

样例已过

A1 WA9

2021/3/31 15:14
加载中...