一道线段树维护区间操作的题 soj4234 01Pairs

【题目链接】

http://cstest.scu.edu.cn/soj/problem.action?id=4234

【题目大意】

给定一个长度为n(n<=10^5)个01序列,定义以下两种操作:

①:将区间[l,r]的序号01翻转,0->1,1->0,如101翻转后为010

②:求区间[l,r]中满足l <= i < j <= r 且 the ith element is 0, jth element is 1

操作次数不超过10^5次。

【分析】

此题维护一个区间上的某些操作,使用线段树比较适合。

 

对于线段树,通常要维护边界[l,r]。

对于操作②,要求出[l,r]满足其条件的,取mid=(l+r)>>1,query[l,r] = query[l,mid]+query[mid+1,r]+[l,mid]中0的个数*[mid+1]中1的个数。

于是还需要维护一个域存储本区间0的个数,同时存储本区间的query值。

对于操作①,要求出反转区间之后区间域的变化,目测翻转后的值和之前的值并没有太大关系,故增加一个域存储反转后的值,这样反转的时候只需要互换这两个值即可。该域值定义如下:

区间[l,r]中满足l <= i < j <= r 且 the ith element is1, jth element is 0。

同时为了实现惰性操作,还需要一个bool变量rev来标记本区间是否需要反转。

于是维护如下域:

    int l , r ;     //边界
    int zeroCnt ;    //[l,r]中0的数目
    unsigned int value ;  //query的值
    unsigned int anti_value ; //反转后的query值
    bool rev ;     //标记是否需要翻转

【代码】

//2012.11.18 线段树 soj karsbin
#include <stdio.h>
#include <ctype.h>
#include <string.h>

const int maxn = 1024 * 100 ;

inline bool get(int &t)
{
    bool flag = 0 ;
    char c;
    while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ;
    if( c == -1 ) return 0 ;
    if(c=='-') flag = 1 , t = 0 ;
    else t = c ^ 48;
    while(isdigit(c = getchar()))    t = (t << 1) + (t << 3) + (c ^ 48) ;
    if(flag) t = -t ;
    return 1 ;
}

struct node {
    int l , r ;					//边界
    int zeroCnt ;				//[l,r]中0的数目
    unsigned int value ;		//query的值
    unsigned int anti_value ;	//反转后的query值
    bool rev ;					//标记是否需要翻转
};

node tree[maxn*4] ;
int top , n , m , len ;
char str[maxn] ;

void change(int root)
{
	if ( tree[root].l < tree[root].r )
	{
		int mid = tree[root*2].r ;
		tree[root].zeroCnt = tree[root*2].zeroCnt + tree[root*2+1].zeroCnt ;
		tree[root].value = tree[root*2].value + tree[root*2+1].value + tree[root*2].zeroCnt * (tree[root*2+1].r-mid-tree[root*2+1].zeroCnt) ;
		tree[root].anti_value = tree[root*2].anti_value + tree[root*2+1].anti_value + (mid-tree[root*2].l+1-tree[root*2].zeroCnt)*tree[root*2+1].zeroCnt ;
	}
}

void build(int root,int l,int r)
{
    tree[root].l = l ;    tree[root].r = r ;
    if ( l == r )
    {
        tree[root].zeroCnt = str[l-1] ^ 49 ;
        tree[root].value = tree[root].anti_value = 0 ;
    }
    else
    {
        int mid = ( l + r ) >> 1 ;
        build(root*2, l, mid);
        build(root*2+1, mid+1, r);
        change(root);
    }
    tree[root].rev = false ;
}

void modify(int root)
{
    if ( tree[root].rev == true )
    {
        tree[root].zeroCnt = tree[root].r - tree[root].l + 1 - tree[root].zeroCnt ;
        
        unsigned int t = tree[root].anti_value ;
        tree[root].anti_value = tree[root].value ;
        tree[root].value = t ;

        if ( tree[root].l < tree[root].r )
        {
            tree[root*2].rev ^= 1 ;
            tree[root*2+1].rev ^= 1 ;
        }
        tree[root].rev = false ;
    }
}

unsigned int query(int root,int l,int r,int& zero, int &one)
{
    modify(root);
    if ( tree[root].l == l && tree[root].r == r )
    {
		zero = tree[root].zeroCnt ;
		one = tree[root].r - tree[root].l + 1 - zero ;
        return tree[root].value ;
    }
    int mid = ( tree[root].l + tree[root].r ) >> 1 ;
    unsigned int ans = 0;
    if( r <= mid ) ans = query(root*2, l, r, zero, one);
    else if( l > mid ) ans = query(root*2+1, l, r, zero, one);
    else 
	{
		int z1, z2 ;
		int o1, o2 ;
		ans = query(root*2, l, mid, z1, o1)+query(root*2+1, mid+1, r, z2, o2);
		zero = z1 + z2 ;
		one = o1 + o2 ;
		ans += z1 * o2 ;
	}

	
	modify(root*2);
	modify(root*2+1);
    change(root);
	
    return ans ;
}

void reverseSeq(int root,int l,int r)
{
    modify(root);
    if ( tree[root].l == l && tree[root].r == r )
    {
        tree[root].rev ^= 1 ;
        modify(root);
        return ;
    }
    int mid = ( tree[root].l + tree[root].r ) >> 1 ;
    if( r <= mid ) reverseSeq(root*2, l, r);
    else if( mid < l ) reverseSeq(root*2+1, l, r);
    else 
    {
        reverseSeq(root*2, l, mid);
        reverseSeq(root*2+1, mid+1, r);
    }
	modify(root*2);
	modify(root*2+1);
    change(root);
}

int main()
{
    int t , op , l , r ;
    get(t);
    while (t--)
    {
        get(n);    get(m);
        scanf("%s",str);
        build(1,1,strlen(str));
        while (m--)
        {
            get(op);    get(l);    get(r);
            l++;r++;
            if ( op == 0 )
            {
                reverseSeq(1,l,r);                    
            }
            else
            {
				int zero , one ;
                printf("%u\n",query(1,l,r,zero,one));
            }
        }
    }
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值