题解 P5057 【[CQOI2006]简单题】
地铁dixiatielu · · 题解
吸氧分块做法
看到TJ里好像没有用分块做的,于是蒟蒻就来发一条用bitset分块的TJ
分块,是一种时间复杂度带根号的做法。虽然劣于线段树和树状数组,导致此题分块需要吸氧才能跑过,但是个人觉得分块代码比线段树好打多了,也比树状数组更容易理解...
题意分析
一个01数组,给出两个操作,分别是区间翻转01和单点查询.要求输出查询结果
思路1-暴力
看到题目,首先想到的就是直接暴力大模拟,实测大模拟加快读可拿80pts
暴力核心代码(
int ccf,a[N],poo;
signed main()
{
qin >> n >> m;
while(m--)
{
qin >> ccf;
if(ccf == 1)
{
qin >> l >> r;
for(reg int i = l;i <= r;i++)
{
a[i] = !a[i];
}
}
else
{
qin >> poo;
printf("%d\n",a[poo]);
}
}
return 0;
}
思路2-优雅的暴力-分块
分块其实真的是一种优雅的暴力
分块的思路
由于暴力明显会T掉,我们可以把数组中的
以下是完整代码
#include <cstdio>
#include <bitset>
#define reg register
using namespace std;
int n,m,l,r,op,pos,lid,rid,llid,rrid,blkid,in_blkid;
const int blk_sze = 320;//320个1块(0~319) 320~639 640~959......
bitset<blk_sze> s[319];
int main()
{
scanf("%d%d",&n,&m);//读入
for(reg int i = 1;i <= m;i++)
{
scanf("%d",&op);//读入
if(op == 1)//操作1
{
scanf("%d%d",&l,&r);//读入左端点和右断点
l--;
r--;//由于我们的块的下标和块内下标都是从0开始,而读入数据默认是从1开始,所以这里l和r都要-1
lid = l / blk_sze;
rid = r / blk_sze;//计算左端点所处块,右端点所处块的编号
for(reg int j = lid + 1;j < rid;j++)
{
s[j].flip();
}//把左右端点之间的块全部直接整块取反
llid = l % blk_sze;//左边界在左端点所处块内的位置
rrid = r % blk_sze;//右边界在右端点所处块内的位置
if(lid != rid)//如果左右边界不在同一块中
{
for(reg int j = llid;j < blk_sze;j++)
{
s[lid].flip(j);
}
for(reg int j = 0;j <= rrid;j++)
{
s[rid].flip(j);
}
}
else//如果左右端点在一个块中
{
for(reg int j = llid;j <= rrid;j++)
{
s[lid].flip(j);//这里的lid也可以改成rid,是一样的
}
}//进行这样的判断是为了防止左右端点在同一块的情况导致那整个块都被直接取反
}
else//操作2
{
scanf("%d",&pos);//读入位置
pos--;
blkid = pos / blk_sze;//pos所处块的编号
in_blkid = pos % blk_sze;//pos所处块的块内位置
printf("%d\n",s[blkid].test(in_blkid));//输出答案
}
}
return 0;
}
最终结果
后言
可能用打标记开数组的那种分块能不吸氧A掉此题?我没试过...不知道有没有神犇能写出来试试