P3870 [TJOI2009]开关

传送门

算法分析

  • 很简单一个题 线段树啥的可以直接碾过去 (瞎搞应该也能过) 但是为了练分块还是打了打分块
  • 首先考虑分块大小 显然还是 √n 大小 然后将bel预处理好 就可以直接做了
  • 对于每次修改 如果左端点和右端点在同一个块里 一样暴力处理 然后暴力处理左端点的块 暴力处理右端点的块 再处理中间端点的块
  • 这里注意记录一个sum 表示当前块一共有多少个灯开着 然后记录一个lazy 表示是否对当前块进行过0处理
  • 显然如果对于一个块我们处理了两次 那么和没处理过一样 所以我们直接每次让lazy ^ 1 就好了
  • 对于每次询问 暴力处理左端点右端点 对于中间部分 如果lazy为1 则让答案加上size - sum 如果lazy为0 就让答案加上sum

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;

int size;
int bel[maxn];
int a[maxn],sum[maxn];
int lazy[maxn];

void change(int l,int r){
	if(bel[l] == bel[r]) {
		for(int i = l;i <= r;++i) {
			a[i] ^= 1;
			if(a[i] == 1) sum[bel[i]]++;
			else sum[bel[i]]--;
		}
		return;
	}
	for(int i = l;i <= bel[l] * size;++i) {
		a[i] ^= 1;
		if(a[i] == 1) sum[bel[i]]++;
		else sum[bel[i]]--;
	}
	for(int i = r;i >= (bel[r] - 1) * size + 1;--i) {
		a[i] ^= 1;
		if(a[i] == 1) sum[bel[i]]++;
		else sum[bel[i]]--;
	}
	for(int i = bel[l] + 1;i <= bel[r] - 1;++i) lazy[i] ^= 1;
}

void ask(int l,int r){
	int ans = 0;
	if(bel[l] == bel[r]){
		for(int i = l;i <= r;++i) ans += a[i] ^ lazy[bel[i]];
		printf("%d\n",ans);
		return;
	}
	for(int i = l;i <= bel[l] * size;++i) ans += a[i] ^ lazy[bel[i]];
	for(int i = r;i >= (bel[r] - 1) * size + 1;--i) ans += a[i] ^ lazy[bel[i]];
	for(int i = bel[l] + 1;i <= bel[r] - 1;++i) {
		if(lazy[i]) ans += size - sum[i];
		else ans += sum[i];
	}
	printf("%d\n",ans);
	return;
}

int main(){
	int n,m;scanf("%d%d",&n,&m);
	size = sqrt(n);
	for(int i = 1;i <= n;++i) bel[i] = (i - 1) / size + 1;
	for(int i = 1;i <= m;++i) {
		int flag,l,r;scanf("%d%d%d",&flag,&l,&r);
		if(flag == 0) change(l,r);
		if(flag == 1) ask(l,r);
	}
	return 0;
}
posted @ 2020-09-07 21:12  HISKrrr  阅读(89)  评论(0编辑  收藏  举报