P2846 USACO08NOV Light Switching G

题目描述

灯是由高科技——外星人鼠标操控的。你只要左击两个灯所连的鼠标,

这两个灯,以及之间的灯都会由暗变亮,或由亮变暗。右击两个灯所连的鼠

标,你就可以知道这两个灯,以及之间的灯有多少灯是亮的。起初所有灯都是暗的,你的任务是在LZ之前算出灯的亮灭。

输入格式

\(1\) 行: 用空格隔开的两个整数 \(N\)\(M\)\(N\) 是灯数

\(2..M+1\) 行: 每行表示一个操作, 有三个用空格分开的整数: 指令号, \(S_i\)\(E_i\)

\(1\) 种指令(用 \(0\) 表示)包含两个数字 \(S_i\)\(E_i\) (\(1\leq S_i\leq E_i \leq N\)), 它们表示起始开关和终止开关。

\(2\) 种指令(用 \(1\) 表示)同样包含两个数字 \(S_i\)\(E_i\) (\(1\leq S_i\leq E_i\leq N\)), 不过这种指令是询问从 \(S_i\)\(E_i\) 之间的灯有多少是亮着的.

输出格式

样例 #1

样例输入 #1

4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4

样例输出 #1

1
2

提示

分析

考虑区间翻转,即区间异或,翻转之后区间的亮灯数等于原区间的暗灯数,且符合结合律,考虑使用线段树

#include<bits/stdc++.h>
using namespace std;
int a[1000005<<2],t[1000005<<2],ad[1000005<<2];
int n,m;
void pushup(int num){t[num]=t[num<<1]+t[num<<1|1];}
void pushdown(int num,int l,int r){
	int mid=l+r>>1;
	if(ad[num]&1){
		t[num<<1]=mid-l+1-t[num<<1],t[num<<1|1]=r-mid-t[num<<1|1];
		ad[num<<1]^=1,ad[num<<1|1]^=1;	
	}
	ad[num]=0;
}
void add(int l,int r,int num,int x,int y,int k){
	if(x>r||y<l)return;
	if(x<=l&&r<=y){
		t[num]=r-l+1-t[num];
		ad[num]^=1;
		return;
	}
	int mid=l+r>>1;
	pushdown(num,l,r);
	add(l,mid,num<<1,x,y,k),add(mid+1,r,num<<1|1,x,y,k);
	pushup(num);
}
int query(int l,int r,int num,int x,int y){
	if(x>r||y<l)return 0;
	if(x<=l&&r<=y)return t[num];
	int mid=l+r>>1;
	pushdown(num,l,r);
	return query(l,mid,num<<1,x,y)+query(mid+1,r,num<<1|1,x,y);
}
int main(){
	cin>>n>>m;
	for(int i=1,opt,x,y;i<=m;i++){
		cin>>opt>>x>>y;
		if(opt==0)add(1,n,1,x,y,1);
		else cout<<query(1,n,1,x,y)<<endl;
	}
	return 0;
}
posted @ 2023-06-24 14:11  alex_liu09  阅读(40)  评论(0)    收藏  举报