【CQOI 2006】 简单题

本文介绍了一种利用线段树及其懒标记特性解决数组区间翻转查询问题的方法。通过递归建立和更新线段树,实现高效区间翻转及查询特定位置值的功能。适用于数据范围较大的场景。

Description

   有一个n个元素的数组,每个元素初始均为0。有m条指令,要么让其中一段连续序列数字反转——0变1,1变0(操作1),要么询问某个元素的值(操作2)。例如当n=20时,10条指令如下: 

Input

   第一行包含两个整数n,m,表示数组的长度和指令的条数,以下m行,每行的第一个数t表示操作的种类。若t=1,则接下来有两个数L, R (L<=R),表示区间[L, R]的每个数均反转;若t=2,则接下来只有一个数I,表示询问的下标。

Output

每个操作2输出一行(非0即1),表示每次操作2的回答。

Sample Input

20 10
1 1 10
2 6
2 12
1 5 12
2 6
2 15
1 6 16
1 11 17
2 12
2 6

Sample Output

1
0
0
0
1
1

Hint

50%的数据满足:1<=n<=1,000,1<=m<=10,000 
100%的数据满足:1<=n<=100,000,1<=m<=500,000


【分析】

        此题很巧妙地运用了线段树的lazy操作。若某一段区间的lazy为true,则表示这一段要修改,lazy下放时左右儿子的lazy取反。

        计算答案的时候只需迭代操作,记录从根节点到叶子节点一路上为true的lazy数,记为t。

        那么每次询问的答案就是t&1。


【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100005;
struct node{
       int a,b;
       bool lazy;
       }Tree[maxn*4];
int N,M;
void _in(int &x)
{
	char t=getchar();
	while(t<'0'||'9'<t) t=getchar();
	for(x=t-'0',t=getchar();'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _BuiltTree(int id,int l,int r)   //建树,确立区间端点 
{
	int mid=(l+r)>>1;
	Tree[id].a=l;Tree[id].b=r;
	if(l<r)
	{
		_BuiltTree((id<<1),l,mid);
		_BuiltTree((id<<1)+1,mid+1,r);
	}
}
void _change(int id,int l,int r)
{
	int mid=(Tree[id].a+Tree[id].b)>>1;
    if((l<=Tree[id].a&&Tree[id].b<=r)||Tree[id].a==Tree[id].b)
    {
        Tree[id].lazy=(!Tree[id].lazy);
        return;
    }
    if(Tree[id].lazy)       //lazy下放 
    {
        Tree[id].lazy=false;
        Tree[(id<<1)].lazy=(!Tree[(id<<1)].lazy);
        Tree[(id<<1)+1].lazy=(!Tree[(id<<1)+1].lazy);
    }
    if(!(r<Tree[(id<<1)].a||Tree[(id<<1)].b<l)) //递归处理左右儿子 
	    _change((id<<1),l,r);
    if(!(r<Tree[(id<<1)+1].a||Tree[(id<<1)+1].b<l)) 
	    _change((id<<1)+1,l,r);
}
int _getans(int id,int i)
{
    int t=0;
    while(Tree[id].a)   //迭代,从根节点走到叶子节点 
    {
        if(Tree[id].lazy) 
		    t++;
		if(i<=Tree[(id<<1)].b)   //去左儿子 
		    id<<=1;
		else                     //去右儿子 
		    id=(id<<1)+1;
    }
    return t&1;
}
void _init()
{
	_in(N);_in(M);
    _BuiltTree(1,1,N);
}
void _out(int x)
{
	x?putchar('1'):putchar('0');
	putchar('\n');
}
void _solve()
{
    for(int i=1;i<=M;i++)
    {
    	int p,l,r;
    	_in(p);
        if(p==1)
        {
        	_in(l);_in(r);
            _change(1,l,r);
        }
        else 
        {
        	_in(l);
        	_out(_getans(1,l));
        }
    }
}
int main()
{	
    _init();
    _solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值