[TJOI2009] 开关 (分块)

本文介绍了一种使用分块技巧处理区间元素反转及查询问题的方法。通过维护块的标记和每个块中1的数量,实现了对零散元素的直接修改和对整块的高效处理。文章详细解释了如何通过异或操作更新块的状态,并提供了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这是我第一个独立完成的分块题, 恭喜1A!!

题目要你完成两个任务:

  • 对区间 [x,y][x,y][x,y] 的所有元素,000 变成 111111 变成 000
  • 查询区间 [x,y][x,y][x,y] 中有多少个 111

很显然就可以用分块做么,(感觉又是套路题)

tag[i]tag[i]tag[i] 表示第 iii 个块要变的次数 x % 2x~\% ~2x % 2

但是我们发现这样处理不是很方便,于是我们令设 ans[i]ans[i]ans[i] 表示第 iii 个块目前有多少个元素是 111

对于修改操作 [x,y][x,y][x,y]

  • 对于零散元素,那么我们直接暴力修改 a[i]a[i]a[i] 并更新 ans[i]ans[i]ans[i]
  • 对于整块,我们对 tag[i]tag[i]tag[i] 异或 111, 那么 ans[i]ans[i]ans[i] 显然被更新为 len−ans[i]len-ans[i]lenans[i]

询问也很简单:

  • 零散元素答案就是 a[i]a[i]a[i] 异或 tag[i]tag[i]tag[i]
  • 整块答案就是 ans[i]ans[i]ans[i]

入门题入门题

int n, m, len, num ;
int a[N], bl[N], l[M], r[M], tag[M], ans[M] ;

void build() {
	len = sqrt(n), num = (n - 1) / len + 1 ;
	rep(i, 1, n) bl[i] = (i - 1) / len + 1 ;
	rep(i, 1, num) l[i] = (i - 1) * len + 1, r[i] = i * len ;
	r[num] = n ;
}

void change(int x, int y) {
	if (bl[x] == bl[y]) {
		rep(i, x, y) {
			ans[bl[x]] -= (a[i] ^ tag[bl[x]]) ;
			a[i] ^= 1 ;
			ans[bl[x]] += (a[i] ^ tag[bl[x]]) ;
		}
	} else {
		rep(i, x, r[bl[x]]) {
			ans[bl[x]] -= (a[i] ^ tag[bl[x]]) ;
			a[i] ^= 1 ;
			ans[bl[x]] += (a[i] ^ tag[bl[x]]) ;
		}
		rep(i, bl[x] + 1, bl[y] - 1) {
			tag[i] ^= 1 ;
			ans[i] = len - ans[i] ;
		}
		rep(i, l[bl[y]], y) {
			ans[bl[y]] -= (a[i] ^ tag[bl[y]]) ;
			a[i] ^= 1 ;
			ans[bl[y]] += (a[i] ^ tag[bl[y]]) ;
		}
	}
}

int query(int x, int y) {
	int s = 0 ;
	if (bl[x] == bl[y]) {
		rep(i, x, y) s += (a[i] ^ tag[bl[x]]) ;
	} else {
		rep(i, x, r[bl[x]]) s += (a[i] ^ tag[bl[x]]) ;
		rep(i, bl[x] + 1, bl[y] - 1) s += ans[i] ;
		rep(i, l[bl[y]], y) s += (a[i] ^ tag[bl[y]]) ;
	}
	return s ;
}

signed main(){
	scanf("%d%d", &n, &m) ;
	build() ;
	rep(i, 1, m) {
		int op, x, y ; scanf("%d%d%d", &op, &x, &y) ;
		if (op == 0) change(x, y) ;
		else printf("%d\n", query(x, y)) ;
	}
	return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值