线段树专题

hdu 1166 敌兵布阵

操作:单点增加或减少,查询区间和.

http://acm.hdu.edu.cn/showproblem.php?pid=1166

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=50010;

struct ST
{
    int l,r;
    int sum;
}st[maxn<<2];

void pushUp(int i)
{
    st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;
}

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    if(st[i].l==st[i].r)
    {
        rd(st[i].sum);
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    pushUp(i);
}

void add(int i,int p,int val)
{
    if(st[i].l==st[i].r)
    {
        st[i].sum+=val;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(p<=mid)
        add(i<<1,p,val);
    else
        add((i<<1)|1,p,val);
    pushUp(i);
}

int query(int i,int L,int R)
{
    if(st[i].l==L&&st[i].r==R)
    {
        return st[i].sum;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(R<=mid)
        return query(i<<1,L,R);
    else if(L>mid)
        return query((i<<1)|1,L,R);
    else
        return query(i<<1,L,mid)+query((i<<1)|1,mid+1,R);
}
int n;
char cm[10];

int main()
{
    int cas=1;
    int t;rd(t);
    while(t--)
    {
        printf("Case %d:\n",cas++);
        rd(n);
        build(1,1,n);
        while(scanf("%s",cm))
        {
            if(cm[0]=='Q')
            {
                int l,r;
                rd2(l,r);
                printf("%d\n",query(1,l,r));
            }
            else if(cm[0]=='A')
            {
                int p,val;
                rd2(p,val);
                add(1,p,val);
            }
            else if(cm[0]=='S')
            {
                int p,val;
                rd2(p,val);
                add(1,p,-val);
            }
            else
                break;
        }
    }
    return 0;
}

 

hdu 1754  I hate it

http://acm.hdu.edu.cn/showproblem.php?pid=1754

操作:单点替换为另一个值,查询区间最大值.

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=200010;

struct ST
{
    int l,r;
    int MAX;
}st[maxn<<2];

void pushUp(int i)
{
    st[i].MAX=max(st[i<<1].MAX,st[(i<<1)|1].MAX);
}

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    if(st[i].l==st[i].r)
    {
        rd(st[i].MAX);
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    pushUp(i);
}

void update(int i,int p,int val)
{
    if(st[i].l==st[i].r)
    {
        st[i].MAX=val;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(p<=mid)
        update(i<<1,p,val);
    else
        update((i<<1)|1,p,val);
    pushUp(i);
}

int query(int i,int L,int R)
{
    if(st[i].l==L&&st[i].r==R)
    {
        return st[i].MAX;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(R<=mid)
        return query(i<<1,L,R);
    else if(L>mid)
        return query((i<<1)|1,L,R);
    else
        return max(query(i<<1,L,mid),query((i<<1)|1,mid+1,R));
}
int n,m;
char cm[5];
int main()
{
    while(rd2(n,m)!=EOF)
    {
        build(1,1,n);
        while(m--)
        {
            scanf("%s",cm);
            if(cm[0]=='Q')
            {
                int l,r;
                rd2(l,r);
                printf("%d\n",query(1,l,r));
            }
            else
            {
                int p,val;
                rd2(p,val);
                update(1,p,val);
            }
        }
    }
    return 0;
}


poj 3468 A Simple Problem with Integers

http://poj.org/problem?id=3468
操作:区间的每个值都增加一个数,查询区间总和。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=100010;

struct ST
{
    int l,r;
    ll lazy;
    ll sum;
}st[maxn<<2];

void pushUp(int i)
{
    st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;
}

void pushDown(int i,int len)
{
    if(st[i].lazy!=0)
    {
        st[i<<1].lazy+=st[i].lazy;
        st[(i<<1)|1].lazy+=st[i].lazy;
        st[i<<1].sum+=(long long)(len-(len>>1))*st[i].lazy;
        st[(i<<1)|1].sum+=(long long)(len>>1)*st[i].lazy;
        st[i].lazy=0;
    }
}

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    st[i].lazy=0;
    if(st[i].l==st[i].r)
    {
        scanf("%I64d",&st[i].sum);
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    pushUp(i);
}

void add(int i,int L,int R,int val)
{
    if(st[i].l==L&&st[i].r==R)
    {
        st[i].sum+=(long long)(st[i].r-st[i].l+1)*val;
        st[i].lazy+=val;
        return;
    }
    pushDown(i,st[i].r-st[i].l+1);
    int mid=(st[i].l+st[i].r)>>1;
    if(R<=mid)
        add(i<<1,L,R,val);
    else if(L>mid)
        add((i<<1)|1,L,R,val);
    else
    {
        add(i<<1,L,mid,val);
        add((i<<1)|1,mid+1,R,val);
    }
    pushUp(i);
}

ll query(int i,int L,int R)
{
    if(st[i].l==L&&st[i].r==R)
    {
        return st[i].sum;
    }
    pushDown(i,st[i].r-st[i].l+1);
    int mid=(st[i].l+st[i].r)>>1;
    if(R<=mid)
        return query(i<<1,L,R);
    else if(L>mid)
        return query((i<<1)|1,L,R);
    else
        return query(i<<1,L,mid)+query((i<<1)|1,mid+1,R);
}

int n,q;
int l,r,val;
char cm;

int main()
{
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        build(1,1,n);
        while(q--)
        {
            scanf("%s",&cm);
            if(cm=='C')
            {
                scanf("%d%d%d",&l,&r,&val);
                add(1,l,r,val);
            }
            else
            {
                scanf("%d%d",&l,&r);
                printf("%I64d\n",query(1,l,r));
            }
        }
    }
    return 0;
}

hdu 1698 just a hook

http://acm.hdu.edu.cn/showproblem.php?pid=1698

操作:指定区间每个值修改为另一个值,求总区间总和。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int maxn=100010;

struct ST
{
    int l,r;
    int lazy;
    int sum;
}st[maxn<<2];

void pushUp(int i)
{
    st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;
}

void pushDown(int i,int len)
{
    if(st[i].lazy!=0)
    {
        st[i<<1].lazy=st[(i<<1)|1].lazy=st[i].lazy;
        st[i<<1].sum=(len-(len>>1))*st[i].lazy;
        st[(i<<1)|1].sum=(len>>1)*st[i].lazy;
        st[i].lazy=0;
    }
}

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    st[i].lazy=0;
    if(st[i].l==st[i].r)
    {
        st[i].sum=1;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    pushUp(i);
}

void update(int i,int l,int r,int val)
{
    if(st[i].l==l&&st[i].r==r)
    {
        st[i].sum=(st[i].r-st[i].l+1)*val;
        st[i].lazy=val;
        return;
    }
    pushDown(i,st[i].r-st[i].l+1);
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        update(i<<1,l,r,val);
    else if(l>mid)
        update((i<<1)|1,l,r,val);
    else
    {
        update(i<<1,l,mid,val);
        update((i<<1)|1,mid+1,r,val);
    }
    pushUp(i);
}

int t,n,q;
int l,r,val;
int cas=1;

int main()
{
    rd(t);
    while(t--)
    {
        rd(n);
        rd(q);
        build(1,1,n);
        while(q--)
        {
            rd3(l,r,val);
            update(1,l,r,val);
        }
        printf("Case %d: The total value of the hook is %d.\n",cas++,st[1].sum);
    }

    return 0;
}


poj 2528 Mayor's posters

http://poj.org/problem?id=2528

有一条1到10000000的线段,然后给指定区间涂颜色,制定区间个数最多为10000个,后面的颜色覆盖前面的颜色,问最后涂完一共可以看见几种颜色。

要用到离散化,比如 [1, 10000]  [2,  99999999]   [10000,20000]这三个区间

端点从小到大排序并去重后得到

1  2 10000 20000 99999999    分别映射到数字 1 2 3 4  5,也就是1->1   2->2    10000->3   20000->4   99999999->5

那么给定的区间就可以变为[1,3]  [2,5]  [3,4] ,原覆盖关系没变,这样区间长度就大大缩短了

但这样容易出错 比如  [1,10]   [1,4]  [6,10]  这组数据,答案应该是3, 但是像前面那样处理后,区间变为[1,4]   [1,2]   [3,4] ,答案是2,处理方法为

端点从小到大排序并去重得到1 4 6 10 ,对于相邻两个数如果差距大于1,那么就再里面随便加上一个数,变为 1 (2) 4 (5) 6 (7)  10

然后映射关系为1->1   2->2  4->3  5->4   6->5   7->6  10->7

那么原区间就变为[1,7]  [ 1,3]   [5,7]   ,答案正确为3

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=10010;
int id[10000005];//离散化以后对应的id,原区间为l,r,离散化后变为id[l],id[r]
int t;
int n;
int x[maxn*3];//4倍的,2倍是一条线段两个端点,1倍是两个端点之间再加一个,就像[1,10] [1,4] [6,10]这样的数据防止错误

struct ST
{
    int l,r;
    int covered;
}st[maxn*16];//要开线段长度的4倍,按理说12就可以,但是re,开了13就可以,迷糊.....

struct poster
{
    int l,r;
}post[maxn];

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    st[i].covered=false;//一开始都是没被覆盖
    if(st[i].l==st[i].r)
        return;
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
}

bool isvisible(int i,int l,int r)
{
    if(st[i].covered==true)//如果该节点代表的区间已被覆盖掉,直接返回
        return false;
    if(st[i].l==l&&st[i].r==r)//找到那一段区间
    {
        st[i].covered=true;
        return true;
    }
    bool ok;
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        ok=isvisible(i<<1,l,r);
    else if(l>mid)
        ok=isvisible((i<<1)|1,l,r);
    else
    {
        bool a=isvisible(i<<1,l,mid);//左一半区间是否被覆盖
        bool b=isvisible((i<<1)|1,mid+1,r);//有一半
        ok=a||b;//有一个为真,Ok就为真,为真代表没有被覆盖
    }
    if(st[i<<1].covered&&st[(i<<1)|1].covered)//向上更新
        st[i].covered=true;
    return ok;
}


int main()
{
    rd(t);
    while(t--)
    {
        rd(n);
        int len=0;
        for(int i=1;i<=n;i++)
        {
            rd2(post[i].l,post[i].r);
            x[len++]=post[i].l;
            x[len++]=post[i].r;
        }
        sort(x,x+len);
        len=unique(x,x+len)-x;//去掉重复
        int tp=len;
        for(int i=1;i<tp;i++)
        {
            if(x[i]-x[i-1]>1)
                x[len++]=x[i-1]+1;//避免 [1,10]  [1,4] [6,10]这样的数据
        }
        sort(x,x+len);
        for(int i=0;i<len;i++)
        {
            id[x[i]]=i+1;
        }
        build(1,1,len);
        int ans=0;
        for(int i=n;i>=1;i--)
        {
            if(isvisible(1,id[post[i].l],id[post[i].r]))
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


zoj 1610 Count the Colors

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1610

在一条线段上染色,颜色的种类用数字表示,后染色的覆盖前面的染色,问最后能看到几种颜色,且每种颜色有多少不连续的段。比如 [1,8]先染成白色,[1,2]再染成红色,[5,8]再染成红色,那么可以看到白色的有一段(中间),红色的有两段。    题目中所有涉及的数的范围都为[0,8000]。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=8010;
int color[maxn];//每个点的颜色
int ans[maxn];//输出的

struct ST
{
    int l,r;
    int flag;//代表的颜色,默认-1
}st[maxn<<2];

void pushUp(int i)
{
    if(st[i<<1].flag==st[(i<<1)|1].flag&&st[i<<1].flag!=-1)
        st[i].flag=st[i<<1].flag;
}

void pushDown(int i)
{
    if(st[i].flag!=-1)
    {
        st[i<<1].flag=st[i].flag;
        st[(i<<1)|1].flag=st[i].flag;
        st[i].flag=-1;//这里是-1,其实是混合色
    }
}

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    st[i].flag=-1;
    if(st[i].l==st[i].r)
        return;
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
}

void update(int i,int l,int r,int val)
{
    if(st[i].flag==val)
        return;
    if(l<=st[i].l&&st[i].r<=r)//因为参数l,r是整个染色的区间,下面的递归也是整个染色的区间,用[1,9]建立线段树,给[1,8]染色模拟一遍就懂了
    {
        st[i].flag=val;
        return;
    }
    pushDown(i);
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        update(i<<1,l,r,val);
    else if(l>mid)
        update((i<<1)|1,l,r,val);
    else
    {
        update(i<<1,l,r,val);
        update((i<<1)|1,l,r,val);
    }
    pushUp(i);
}

void query(int i)
{
    if(st[i].flag!=-1)
    {
        for(int j=st[i].l;j<=st[i].r;j++)
            color[j]=st[i].flag;
        return;
    }
    if(st[i].l==st[i].r)//父节点的flag-1,那么子节点的flag有可能-1,也有可能不是-1,加这一句话是-1的情况,没有染色
        return;
    query(i<<1);
    query((i<<1)|1);
}

int n,N=8000;
int l,r,val;

int main()
{
    while(rd(n)!=EOF)
    {
        build(1,0,N);
        memset(color,-1,sizeof(color));
        memset(ans,0,sizeof(ans));
        while(n--)
        {
            rd3(l,r,val);
            update(1,l,r-1,val);//为了避免 [1,2] [3,4]这样的情况,如果是相同的颜色,那么应该是两段[2,3]是空白
        }
        query(1);
        int pre=-1;
        for(int i=0;i<N;i++)
        {
            if(pre!=color[i])
            {
                pre=color[i];
                if(pre==-1)
                    continue;
                ans[color[i]]++;
            }
        }
        for(int i=0;i<N;i++)
            if(ans[i])
            printf("%d %d\n",i,ans[i]);
        printf("\n");
    }
    return 0;
}


poj 3264 Balanced Lineup

http://poj.org/problem?id=3264

求区间内最大值与最小值的差。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=50010;

struct ST
{
    int l,r;
    int MAX,MIN;
}st[maxn<<2];

void pushUp(int i)
{
    st[i].MAX=max(st[i<<1].MAX,st[(i<<1)|1].MAX);
    st[i].MIN=min(st[i<<1].MIN,st[(i<<1)|1].MIN);
}

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    if(st[i].l==st[i].r)
    {
        rd(st[i].MAX);
        st[i].MIN=st[i].MAX;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    pushUp(i);
}

int query1(int i,int l,int r)
{
    if(st[i].l==l&&st[i].r==r)
    {
        return st[i].MAX;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        return query1(i<<1,l,r);
    else if(l>mid)
        return query1((i<<1)|1,l,r);
    else
    {
        return max(query1(i<<1,l,mid),query1((i<<1)|1,mid+1,r));
    }
}

int query2(int i,int l,int r)
{
    if(st[i].l==l&&st[i].r==r)
    {
        return st[i].MIN;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        return query2(i<<1,l,r);
    else if(l>mid)
        return query2((i<<1)|1,l,r);
    else
    {
        return min(query2(i<<1,l,mid),query2((i<<1)|1,mid+1,r));
    }
}

int MAX,MIN;
void query(int i,int l,int r)
{
    if(st[i].MAX<MAX&&st[i].MIN>MIN)
        return;
    if(st[i].l==l&&st[i].r==r)
    {
        MAX=max(st[i].MAX,MAX);
        MIN=min(st[i].MIN,MIN);
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        query(i<<1,l,r);
    else if(l>mid)
        query((i<<1)|1,l,r);
    else
    {
        query(i<<1,l,mid);
        query((i<<1)|1,mid+1,r);
    }
}

int n,q;
int l,r;

int main()
{
    while(rd2(n,q)!=EOF)
    {
        build(1,1,n);
        while(q--)
        {
            rd2(l,r);
             MAX=-1,MIN=1000002;
           // printf("%d\n",query1(1,l,r)-query2(1,l,r));这个也可以
           query(1,l,r);
           printf("%d\n",MAX-MIN);
        }
    }
    return 0;
}


hdu 4027 Can you answer these queries?

http://acm.hdu.edu.cn/showproblem.php?pid=4027

操作:给定区间,将区间内的每个值都变为原来的平方根(去整),查询区间和。long long

当一个数为0或者为1时,该数就不用再被更新了,用allone表示该数或者该区间是否需要更新,当allone==1是,表示不用更新,碰到该节点,直接返回就可以。

题目给出的区间端点x,y,没有说明x<=y,得判断一下.....

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=100010;

struct ST
{
    int l,r;
    ll sum;
    bool allone;
}st[maxn<<2];

void pushUp(int i)
{
    st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;
    st[i].allone=st[i<<1].allone&&st[(i<<1)|1].allone;
}

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    if(st[i].l==st[i].r)
    {
        scanf("%I64d",&st[i].sum);
        if(st[i].sum==0||st[i].sum==1)
            st[i].allone=1;
        else
            st[i].allone=0;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    pushUp(i);
}

void cal(int i,int l,int r)
{
    //if(st[i].sum==(st[i].r-st[i].l+1))//数据中如果每个endurance值不为0的话可以这样判断,这个区间每个点都为1,就不用再更新
       // return;
    if(st[i].allone==1)//该区间内全是1,不用再更新了
        return;
    if(st[i].l==st[i].r)//找到该数
    {
        st[i].sum=(ll)sqrt(1.0*st[i].sum);
        if(st[i].sum==1)
            st[i].allone=1;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        cal(i<<1,l,r);
    else if(l>mid)
        cal((i<<1)|1,l,r);
    else
    {
        cal(i<<1,l,mid);
        cal((i<<1)|1,mid+1,r);
    }
    pushUp(i);
}

ll query(int i,int l,int r)
{
    if(st[i].l==l&&st[i].r==r)
    {
        return st[i].sum;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        return query(i<<1,l,r);
    else if(l>mid)
        return query((i<<1)|1,l,r);
    else
    {
        return query(i<<1,l,mid)+query((i<<1)|1,mid+1,r);
    }
}

int n,q;
int cm,l,r;
int c=1;

int main()
{
    while(rd(n)!=EOF)
    {
        printf("Case #%d:\n",c++);
        build(1,1,n);
        rd(q);
        while(q--)
        {
            rd3(cm,l,r);
            if(l>r)
                swap(l,r);//陷阱!
            if(!cm)
            {
                cal(1,l,r);
            }
            else
                printf("%I64d\n",query(1,l,r));
        }
        printf("\n");
    }
    return 0;
}


poj 2892  Tunnel Warfare

http://poj.org/problem?id=2892

一开始有连续的n个1,位置分别是1-n,三种操作,一是将某个位置的1变为0,而是将某个位置的0修复为1,三是查询包含某个位置的最大连续区间(该区间内都是1)的长度.

样例

7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4

一开始7个位置全是1,   1 1 1 1 1 1 1, D 3表示将第3个位置设为0,即 1 1 0 1 1 1 1,D 6, 1 1 0 1 1 0 1,D 5 ,   1 1 0 1 0 0 1, 

Q 4表示查询包含位置4的最大连续区间(全为1), 答案为1,Q 5答案为0, 都是在 1 1 0 1 0 0 1中看出来的

R表示将最后一个设为0的位置修复为1,也就是将第5个位置修复为1 , 即  1 1 0 1 1 0 1,Q 4 ,答案为2

再一个R表示将第6个位置修复为1,1 1 0 1 1 1 1,Q4, 答案为4

想法在注释中。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=50010;

struct ST
{
    int l,r;
    int ls,rs,ms;//区间左端多少个连续1,右端多少个连续1,该区间最大连续1长多少,即左端连续区间,右端连续区间,最大连续区间
}st[maxn<<2];

void build(int i,int l,int r)
{
    st[i].l=l;
    st[i].r=r;
    if(st[i].l==st[i].r)
    {
        st[i].ls=1;st[i].rs=1;st[i].ms=1;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    st[i].ls=st[i].rs=st[i].ms=st[i].r-st[i].l+1;
}

void update(int i,int p,char c)//位置为p,命令为c
{
    if(st[i].l==st[i].r)
    {
        if(c=='D')//破坏
            st[i].ls=st[i].rs=st[i].ms=0;
        else//修复
            st[i].ls=st[i].rs=st[i].ms=1;
        return;
    }
    int mid=(st[i].l+st[i].r)>>1;
    if(p<=mid)
        update(i<<1,p,c);
    else
        update((i<<1)|1,p,c);
    st[i].ls=st[i<<1].ls;//孩子节点左连续区间肯定是父节点的左端连续区间的一部分
    st[i].rs=st[(i<<1)|1].rs;//右连续区间
    st[i].ms=max(max(st[i<<1].ms,st[(i<<1)|1].ms),st[i<<1].rs+st[(i<<1)|1].ls);
    //父亲节点的最大连续区间为  左孩子最大连续区间,右孩子最大连续区间,中间连续最大区间三者中的最大值

    //如果左孩子都是连续区间,那么父节点的左连续区间除了左孩子的左连续区间,还要加上右孩子的左连续区间
    if(st[i<<1].ls==st[i<<1].r-st[i<<1].l+1)
    {
        st[i].ls+=st[(i<<1)|1].ls;
    }
    //如果右孩子都是连续区间,那么父节点的右连续区间除了右孩子的右连续区间,还要加上左孩子的右连续区间
    if(st[(i<<1)|1].rs==st[(i<<1)|1].r-st[(i<<1)|1].l+1)
    {
        st[i].rs+=st[i<<1].rs;
    }
}

int query(int i,int p)
{
    //该区间没有1或者找到该位置或者该区间全是1
    if(st[i].ms==0||st[i].l==st[i].r||st[i].ms==st[i].r-st[i].l+1)
        return st[i].ms;
    int mid=(st[i].l+st[i].r)>>1;

    if(p<=mid)//向左孩子中查询
    {
        if(p>=st[i<<1].r-st[i<<1].rs+1) //st[i<<1].r-st[i<<1].rs+1为左孩子节点右连续区间的左边界,比如[6,8]的长度为3,那么左边界就为8-3+1=6
            return query(i<<1,p)+query((i<<1)|1,mid+1);
        else
            return query(i<<1,p);
        //如果位置p在左孩子节点的右连续区间中,那么还要加上右孩子节点的左连续区间
        //比如左孩子[1,4],其右连续区间为[2,4],右孩子节点为[5,7],右孩子左连续区间为[5,6],位置p为3,那么包括3的连续区间不仅在[2,4]中,
        //还在[5,6]中,即包括3的最大连续区间为[2,6]
    }
    else//在右孩子中查询
    {
        if(p<=st[(i<<1)|1].l+st[(i<<1)|1].ls-1)
            return query((i<<1)|1,p)+query(i<<1,mid);
        else
            return query((i<<1)|1,p);
        //如果位置p在右孩子节点的左连续区间中,还要加上左孩子节点的右连续区间
    }
}

int n,m,p;
char cm[2];
stack<int>sta;

int main()
{
    while(rd2(n,m)!=EOF)
    {
        while(!sta.empty())
            sta.pop();
        build(1,1,n);
        while(m--)
        {
            scanf("%s",cm);
            if(cm[0]=='D')
            {
                rd(p);
                update(1,p,'D');
                sta.push(p);
            }
            else if(cm[0]=='R')
            {
                p=sta.top();
                sta.pop();
                update(1,p,'R');
            }
            else
            {
                rd(p);
                printf("%d\n",query(1,p));
            }
        }
    }
    return 0;
}

hdu 3974 Assign the task

http://acm.hdu.edu.cn/showproblem.php?pid=3974

有n个员工,编号1-n,存在一些上司下属关系, a是c的上司,c是b的上司,那么a也是b的上司,没有上司的员工成为领导。给出n-1条上司下属关系,那么这n个员工关系就组成了一棵树,当给编号为i的员工分配任务时,该员工和其所有的下属都接受刚分配的任务,放弃前一个任务(如果有的话),查询编号为i的员工当前正做着哪一个任务。任务由数字表示。

在树中,父亲节点以及其所有的子节点是同时更新的,我们首先要把这些节点映射到区间上去,用dfs给这些点重新编号,start[i]表示该节点的新编号,end[i]表示其所有子节点的最后一个编号,那么执行更新操作时,只要对区间[start[i], end [i] ] 进行更新就可以了,把该区间的任务替换为新任务。

线段树专题_第1张图片

只要把树映射到线段区间上,就好操作了,建立线段树对其操作,区间更新,查询单点值,lazy表示分配的任务。

#define rd(x) scanf("%d",&x)
#define rd2(x,y)  scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=50010;

int number;//给节点编号
int start[maxn],end[maxn];//第i个节点的编号,以及其孩子节点的最后一个编号,要更新,就更新这一段区间,对一个点操作,也就是对一段区间操作
vector<int>vc[maxn];//邻接表存边

void init()
{
    number=0;
    for(int i=0;i<maxn;i++)
        vc[i].clear();
}

void dfs(int u)//编号
{
    ++number;
    start[u]=number;
    int len=vc[u].size();
    for(int i=0;i<len;i++)
    {
        dfs(vc[u][i]);
    }
    end[u]=number;
}

struct ST
{
    int l,r;
    int lazy;//分配的是什么任务
}st[maxn<<2];

void pushDown(int i)
{
    if(st[i].lazy!=-1)
    {
        st[i<<1].lazy=st[(i<<1)|1].lazy=st[i].lazy;
        st[i].lazy=-1;
    }
}

void build(int i,int l,int r)
{
    st[i].l=l;st[i].r=r;
    st[i].lazy=-1;
    if(st[i].l==st[i].r)
        return;
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
}

void update(int i,int l,int r,int val)
{
    if(st[i].l==l&&st[i].r==r)
    {
        st[i].lazy=val;
        return;
    }
    pushDown(i);
    int mid=(st[i].l+st[i].r)>>1;
    if(r<=mid)
        update(i<<1,l,r,val);
    else if(l>mid)
        update((i<<1)|1,l,r,val);
    else
    {
        update(i<<1,l,mid,val);
        update((i<<1)|1,mid+1,r,val);
    }
}

int query(int i,int p)
{
    if(st[i].l==st[i].r)
        return st[i].lazy;
    pushDown(i);
    int mid=(st[i].l+st[i].r)>>1;
    if(p<=mid)
        return query(i<<1,p);
    else
        return query((i<<1)|1,p);
}
int n,m;
char cm[2];
int cas=1;
int u,v;
bool vis[maxn];

int main()
{
    int t;
    rd(t);
    while(t--)
    {
        printf("Case #%d:\n",cas++);
        rd(n);
        init();
        memset(vis,0,sizeof(vis));
        for(int i=1;i<n;i++)
        {
            rd2(u,v);
            vc[v].push_back(u);
            vis[u]=1;
        }
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])//找到根节点,只有根节点没有被访问过
            {
                dfs(i);
                break;
            }
        }
        build(1,1,number);
        rd(m);
        while(m--)
        {
            scanf("%s",cm);
            if(cm[0]=='C')
            {
                rd(u);
                printf("%d\n",query(1,start[u]));//start[u]是该节点在线段中的编号
            }
            else
            {
                rd2(u,v);
                update(1,start[u],end[u],v);
            }
        }
    }
    return 0;
}

sdut 2880 Devour Magic

http://www.sdutacm.org/sdutoj/problem.php?action=showproblem&problemid=2880

线段树操作:区间更新,总区间同时增加一个数,查询指定区间的和,查询后将该区间清0.

用到了两个lazy,一个是增量,一个是是否清0,Pushdown的时候,清0的lazy优先。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <cmath>
#include <iomanip>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cctype>
using namespace std;
typedef long long ll;
const int maxn=100010;

struct ST
{
    int l,r;
    ll sum;
    ll lazy;//懒惰标记
    ll lazy0;//该区间是否清0了
}st[maxn<<2];

void pushUp(int i)
{
    st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum;
}

void pushDown(int i,int len)
{
    if(st[i].lazy0!=0)
    {
        st[i<<1].lazy0=st[(i<<1)|1].lazy0=st[i].lazy0;
        st[i<<1].sum=0;
        st[(i<<1)|1].sum=0;
        st[i<<1].lazy=0;
        st[(i<<1)|1].lazy=0;
        st[i].lazy0=0;
    }
    if(st[i].lazy!=0)
    {
        st[i<<1].lazy+=st[i].lazy;
        st[(i<<1)|1].lazy+=st[i].lazy;
        st[i<<1].sum+=ll(len-(len>>1))*st[i].lazy;
        st[(i<<1)|1].sum+=ll(len>>1)*st[i].lazy;
        st[i].lazy=0;
    }
}

void build(int i,int l,int r)
{
    st[i].l=l;st[i].r=r;
    st[i].lazy=st[i].lazy0=0;
    st[i].sum=0;
    if(st[i].l==st[i].r)
        return;
    int mid=(st[i].l+st[i].r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
}

void update(int i,int l,int r,int val)
{
    if(val!=0)
    {
        if(st[i].l==l&&st[i].r==r)
        {
            st[i].lazy+=val;
            st[i].sum+=ll(r-l+1)*val;
            return;
        }
    }
    else
    {
        if(st[i].l==l&&st[i].r==r)
        {
            st[i].sum=0;
            st[i].lazy0=1;
            st[i].lazy=0;//保证当前节点的维护的值正确,别忘了这一句
            return;
        }
        pushDown(i,st[i].r-st[i].l+1);
        int mid=(st[i].l+st[i].r)>>1;
        if(r<=mid)
            update(i<<1,l,r,val);
        else if(l>mid)
            update((i<<1)|1,l,r,val);
        else
        {
            update(i<<1,l,mid,val);
            update((i<<1)|1,mid+1,r,val);
        }
        pushUp(i);
    }
}

ll query(int i,int l,int r)
{
    if(st[i].l==l&&st[i].r==r)
    {
        return st[i].sum;
    }
    int mid=(st[i].l+st[i].r)>>1;
    pushDown(i,st[i].r-st[i].l+1);
    if(r<=mid)
        return query(i<<1,l,r);
    else if(l>mid)
        return query((i<<1)|1,l,r);
    else
        return query(i<<1,l,mid)+query((i<<1)|1,mid+1,r);
}
int t[maxn];
int n,q;
ll ans;

int main()
{
    int cas;
    t[0]=0;
    scanf("%d",&cas);
    while(cas--)
    {
        ans=0;
        scanf("%d%d",&n,&q);
        build(1,1,n);
        for(int i=1;i<=q;i++)
        {
            int l,r;
            scanf("%d%d%d",&t[i],&l,&r);
            update(1,1,n,t[i]-t[i-1]);
            ans+=query(1,l,r);
            update(1,l,r,0);
        }
        //printf("%I64d\n",ans);
        cout<<ans<<endl;
    }
    return 0;
}


 



 

你可能感兴趣的:(线段树专题)