洛谷 P3870 [TJOI2009]开关

洛谷 P3870 [TJOI2009]开关

洛谷传送门

题目描述

现有 nn 盏灯排成一排,从左到右依次编号为:11,22,……,nn。然后依次执行 mm 项操作。

操作分为两种:

  1. 指定一个区间 [a,b][a,b],然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);
  2. 指定一个区间 [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;
}
posted @ 2020-11-26 16:33  Seaway-Fu  阅读(96)  评论(0编辑  收藏  举报