hdu3397 线段树

这道题关键点在于标记  延时的先后

pre0 pre1为每个节点前驱连续最大的0  1

after0 after1 为每个节点后驱连续最大的0 1

Max0 Max1为每个节点连最大连续0 1

count0 count1为每个节点0 1 个数

flash0 flash1为该节点子节点转换   置0或1 的标记

****应为转换对置01没影响  所以在判断时线判断flash1  再判断flash0 在进行转换操作时 是对是不会对flash1有影响的 而在值01时  是会对flash1有影响的   具体看代码

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define LL(x) (x<<1)
#define RR(x) ((x<<1)|1)

struct node
{
    int pre0,pre1,after0,after1;
    int Max1,Max0;
    int count1,count0;
    int flash1,flash2;
}num[4*100000];
int max(int a,int b)
{
    return a>b?a:b;
}
int min(int a,int b)
{
    return a<b?a:b;
}
int deal(int L,int R,int mark)
{
    num[mark].pre0=num[mark].after0=num[mark].Max0=num[mark].flash1=num[mark].count0=0;
    num[mark].pre1=num[mark].after1=num[mark].Max1=num[mark].count1=R-L+1;
    num[mark].flash2=-1;
    if(L==R) return 0;
    int mid=(L+R)/2;
    deal(L,mid,LL(mark));
    deal(mid+1,R,RR(mark));
    return 0;
}
int change1(int a)
{
    int k=num[a].pre0;
    num[a].pre0=num[a].pre1;
    num[a].pre1=k;
    k=num[a].after0;
    num[a].after0=num[a].after1;
    num[a].after1=k;
    k=num[a].count1;
    num[a].count1=num[a].count0;
    num[a].count0=k;
    k=num[a].Max1;
    num[a].Max1=num[a].Max0;
    num[a].Max0=k;
    num[a].flash1=!num[a].flash1;
    //num[a].flash2=-1;
    return 0;
}
int change2(int L,int R,int a,int b)
{
    if(b==0)
    {
        num[a].pre0=num[a].after0=num[a].Max0=num[a].count0=R-L+1;
        num[a].pre1=num[a].after1=num[a].Max1=num[a].count1=0;
    }
    else
    {
        num[a].pre0=num[a].after0=num[a].Max0=num[a].count0=0;
        num[a].pre1=num[a].after1=num[a].Max1=num[a].count1=R-L+1;
    }
    num[a].flash2=b;
    num[a].flash1=0;
    return 0;
}
int update(int L,int R,int left,int right,int mark,int k)
{
    if(L==left&&R==right)
    {
        if(k==2) change1(mark);
        else change2(L,R,mark,k);
        return 0;
    }
    int mid=(L+R)/2;
    if(num[mark].flash2>=0)
    {
        change2(L,mid,LL(mark),num[mark].flash2);
        change2(mid+1,R,RR(mark),num[mark].flash2);
        num[mark].flash2=-1;
    }
    if(num[mark].flash1)
    {
        change1(LL(mark));
        change1(RR(mark));
        num[mark].flash1=0;
    }
    if(right<=mid)
    {
        update(L,mid,left,right,LL(mark),k);
    }
    else if(left>mid)
    {
        update(mid+1,R,left,right,RR(mark),k);
    }
    else
    {
        update(L,mid,left,mid,LL(mark),k);
        update(mid+1,R,mid+1,right,RR(mark),k);
    }
    num[mark].Max0=max(num[LL(mark)].Max0,num[RR(mark)].Max0);
    num[mark].Max0=max(num[LL(mark)].after0+num[RR(mark)].pre0,num[mark].Max0);
    num[mark].Max1=max(num[LL(mark)].Max1,num[RR(mark)].Max1);
    num[mark].Max1=max(num[LL(mark)].after1+num[RR(mark)].pre1,num[mark].Max1);
    num[mark].pre0=num[LL(mark)].pre0;
    if(num[LL(mark)].pre0==mid-L+1) num[mark].pre0+=num[RR(mark)].pre0;
    num[mark].pre1=num[LL(mark)].pre1;
    if(num[LL(mark)].pre1==mid-L+1) num[mark].pre1+=num[RR(mark)].pre1;
    num[mark].after0=num[RR(mark)].after0;
    if(num[RR(mark)].after0==R-mid) num[mark].after0+=num[LL(mark)].after0;
    num[mark].after1=num[RR(mark)].after1;
    if(num[RR(mark)].after1==R-mid) num[mark].after1+=num[LL(mark)].after1;
    num[mark].count1=num[LL(mark)].count1+num[RR(mark)].count1;
    num[mark].count0=num[LL(mark)].count0+num[RR(mark)].count0;
    return 0;
}
int find1(int L,int R,int left,int right,int mark)
{
    if(L==left&&R==right)
    {
        return num[mark].Max1;
    }
    int mid=(R+L)/2;
    if(num[mark].flash2>=0)
    {
        change2(L,mid,LL(mark),num[mark].flash2);
        change2(mid+1,R,RR(mark),num[mark].flash2);
        num[mark].flash2=-1;
    }
    if(num[mark].flash1)
    {
        change1(LL(mark));
        change1(RR(mark));
        num[mark].flash1=0;
    }
    if(right<=mid)
    {
        return find1(L,mid,left,right,LL(mark));
    }
    else if(left>mid)
    {
        return find1(mid+1,R,left,right,RR(mark));
    }
    else
    {
        int t1=find1(L,mid,left,mid,LL(mark));
        int t2=find1(mid+1,R,mid+1,right,RR(mark));
        return max(max(min(t1,num[LL(mark)].after1)+min(num[RR(mark)].pre1,t2),t1),t2);
    }
}
int find2(int L,int R,int left,int right,int mark)
{
    if(L==left&&R==right)
    {
        return num[mark].count1;
    }
    int mid=(L+R)/2;
    if(num[mark].flash2>=0)
    {
        change2(L,mid,LL(mark),num[mark].flash2);
        change2(mid+1,R,RR(mark),num[mark].flash2);
        num[mark].flash2=-1;
    }
    if(num[mark].flash1)
    {
        change1(LL(mark));
        change1(RR(mark));
        num[mark].flash1=0;
    }
    if(right<=mid)
    {
        return find2(L,mid,left,right,LL(mark));
    }
    else if(left>mid)
    {
        return find2(mid+1,R,left,right,RR(mark));
    }
    else return find2(L,mid,left,mid,LL(mark))+find2(mid+1,R,mid+1,right,RR(mark));
}
int main()
{
    int n,m,i,j,a,b,c,T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        n-=1;
        deal(0,n,1);
        for(i=0;i<=n;i++)
        {
            scanf("%d",&a);
            if(a==0) update(0,n,i,i,1,2);
        }
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(a<3)
            {
                update(0,n,b,c,1,a);
            }
            else
            {
                if(a==3) printf("%d\n",find2(0,n,b,c,1));
                else printf("%d\n",find1(0,n,b,c,1));
            }
        }
    }
    return 0;
}
时间: 2024-11-05 09:14:11

hdu3397 线段树的相关文章

hdu-3397 Sequence operation 线段树多种标记

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3397 题目大意: 0 a b表示a-b区间置为0 1 a b表示a-b区间置为1 2 a b表示a-b区间中的0变成1,1变成0 3 a b表示a-b区间中的1的数目 4 a b表示a-b区间中最长连续1的长度 解题思路: 线段树多种标记. 需要处理的东西比较多: 做题的时候发现一个问题: 我的宏定义Max不可以用于函数,尤其是递归函数,这样会使得同一函数重复调用好几遍,递归函数的话更会超时. 1

线段树题目总结

一.单点更新 1.hdu1166 敌兵布阵:有N个兵营,每个兵营都给出了人数ai(下标从1开始),有四种命令,(1)"Addij",表示第i个营地增加j人.(2)"Sub i j",表示第i个营地减少j人.(3)"Query ij",查询第i个营地到第j个营地的总人数.(4)"End",表示命令结束.解题报告Here. 2.hdu1754 I Hate It:给你N个数,M个操作,操作分两类.(1)"QAB"

数据结构---线段树

线段树 转载请注明出处,谢谢!http://blog.csdn.net/metalseed/article/details/8039326  持续更新中···   一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树

(转载)线段树模板(来自胡浩大牛)

http://www.notonlysuccess.com/(今天看二叉树,想回来看看,发现大牛博客进不去...) 如果要学,就要好好学.我copy的,如有错,请看http://www.cnblogs.com/Mu-Tou/archive/2011/08/11/2134427.html [完全版]线段树 很早前写的那篇线段树专辑至今一直是本博客阅读点击量最大的一片文章,当时觉得挺自豪的,还去pku打广告,但是现在我自己都不太好意思去看那篇文章了,觉得当时的代码风格实在是太丑了,很多线段树的初学者

线段树总结 (转载 里面有扫描线类 还有NotOnlySuccess线段树大神的地址)

转载自:http://blog.csdn.net/shiqi_614/article/details/8228102 之前做了些线段树相关的题目,开学一段时间后,想着把它整理下,完成了大牛NotOnlySuccess的博文“完全版线段树”里的大部分题目,其博文地址Here,然后也加入了自己做过的一些题目.整理时,更新了之前的代码风格,不过旧的代码仍然保留着. 同样分成四类,不好归到前四类的都分到了其他.树状数组能做,线段树都能做(如果是内存限制例外),所以也有些树状数组的题目,会标示出来,并且放

线段树——转

  一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍 2:基本操作(demo用的是查询区间最小值) 线段树的主要操作有: (1):线段树的构造 void build(int node,

线段树(转)

线段树   转载请注明出处,谢谢!http://blog.csdn.net/metalseed/article/details/8039326  持续更新中···   一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)

[BZOJ4942][Noi2017]整数 题目描述去uoj 题解:如果只有加法,那么直接暴力即可...(因为1的数量最多nlogn个) 先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依次更新这log位,如果最高位依然有进位,那么找到最高位后面的第一个0,将中间的所有1变成0,那个0变成1.这个显然要用到线段树,但是复杂度是nlog2n的,肯定过不去. 于是我在考场上yy了一下,这log位是连续的,我们每次都要花费log的时间去修改一个岂不是很浪费?我们可以先在线段树上找到这段区间