洛谷 P3870 [TJOI2009]开关
洛谷 P3870 [TJOI2009]开关
题目描述
现有 nn 盏灯排成一排,从左到右依次编号为:11,22,……,nn。然后依次执行 mm 项操作。
操作分为两种:
- 指定一个区间 [a,b][a,b],然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);
- 指定一个区间 [a,b][a,b],要求你输出这个区间内有多少盏灯是打开的。
灯在初始时都是关着的。
输入格式
第一行有两个整数 nn 和 mm,分别表示灯的数目和操作的数目。
接下来有 mm 行,每行有三个整数,依次为:cc、aa、bb。其中 cc 表示操作的种类。
- 当 cc 的值为 00 时,表示是第一种操作。
- 当 cc 的值为 11 时,表示是第二种操作。
aa 和 bb 则分别表示了操作区间的左右边界。
输出格式
每当遇到第二种操作时,输出一行,包含一个整数,表示此时在查询的区间中打开的灯的数目。
题解:
多种数据结构的练手题。
我是为了练分块才来做的这道题。以前一直以为自己会写分块,但是很多细节的部分还是不太会维护。
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int n,m;
int block;
int a[maxn],b[maxn];
int ans[maxn],lazy[maxn];
void update(int x,int y)
{
for(int i=x;i<=min(y,b[x]*block);i++)
{
ans[b[x]]-=a[i]^lazy[b[x]];
a[i]^=1;
ans[b[x]]+=a[i]^lazy[b[x]];
}
if(b[x]!=b[y])
{
for(int i=(b[y]-1)*block+1;i<=y;i++)
{
ans[b[y]]-=a[i]^lazy[b[y]];
a[i]^=1;
ans[b[y]]+=a[i]^lazy[b[y]];
}
}
for(int i=b[x]+1;i<=b[y]-1;i++)
{
lazy[i]^=1;
ans[i]=block-ans[i];
}
}
int query(int x,int y)
{
int ret=0;
for(int i=x;i<=min(y,b[x]*block);i++)
ret+=a[i]^lazy[b[i]];
if(b[x]!=b[y])
for(int i=(b[y]-1)*block+1;i<=y;i++)
ret+=a[i]^lazy[b[y]];
for(int i=b[x]+1;i<=b[y]-1;i++)
ret+=ans[i];
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
block=sqrt(n);
for(int i=1;i<=n;i++)
b[i]=(i-1)/block+1;//注意是i-1
for(int i=1;i<=m;i++)
{
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
if(!opt)
update(x,y);
else
printf("%d\n",query(x,y));
}
return 0;
}