Mango DS Traning #49 ---线段树3 解题手记

Training address: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=38994#overview

B.Xenia and Bit Operations ----Codeforces 339D

线段树大水题。。每个节点维护一个flag,flag=1表示此时应与其兄弟节点做或(|)操作,flag=2表示做异或(^)操作,然后pushup...

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 140010

struct node
{
    int sum,flag;
}tree[4*N];

int two_n(int mi)
{
    int i;
    int res = 1;
    for(i=1;i<=mi;i++)
    {
        res *= 2;
    }
    return res;
}

int a[N];

void pushup(int rt)
{
    if(tree[2*rt].flag == 1)
    {
        tree[rt].sum = tree[2*rt].sum | tree[2*rt+1].sum;
        tree[rt].flag = 2;
    }
    else
    {
        tree[rt].sum = tree[2*rt].sum ^ tree[2*rt+1].sum;
        tree[rt].flag = 1;
    }
}

void build(int l,int r,int rt)
{
    if(l == r)
    {
        tree[rt].sum = a[l];
        tree[rt].flag = 1;
        return;
    }
    int mid = (l+r)/2;
    build(l,mid,2*rt);
    build(mid+1,r,2*rt+1);
    pushup(rt);
}

void update(int l,int r,int pos,int val,int rt)
{
    if(l == r)
    {
        tree[rt].sum = val;
        tree[rt].flag = 1;
        return;
    }
    int mid = (l+r)/2;
    if(pos<=mid)
        update(l,mid,pos,val,2*rt);
    else
        update(mid+1,r,pos,val,2*rt+1);
    pushup(rt);
}

int main()
{
    int n,m;
    int i,pos,val;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int ken = two_n(n);
        for(i=1;i<=ken;i++)
        {
            scanf("%d",&a[i]);
        }
        build(1,ken,1);
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&pos,&val);
            update(1,ken,pos,val,1);
            printf("%d
",tree[1].sum);
        }
    }
    return 0;
}
View Code

D.Copying Data ----Codeforces 292E

这题初看像线段树题,结果就是线段树题。怎么做树,维护哪些值呢? 开始想歪了,参照了别人的报告,于是知道,可以维护stx,sty值,这些值只在叶子节点上表现出来,因为只查叶子节点,不查区间,stx记录这个节点有没有被copy成a数组中的元素,stx == 0则代表还没被a给拷贝过来,否则,stx记录的是从x点开始拷贝k个的那个x值,等会后面要用到。sty则是随stx,stx要更新时,sty也要更新,sty初始为0,这时的sty为拷贝从b的y个位置开始的那个y值,总之,即输入x,y,k,stx记录x,sty记录y。最后查询pos节点,如果其stx或sty为0,说明这个节点还没有受到“侵染“,保持原来的b数组中的值,所以输出b[pos]。否则,已经收到”侵染“,输出a[pos-sty+stx],即从a中去与距离相等的元素,距离就是那个pos-sty,这时知道sty的作用了吧。。

代码:

/*3900KB 280ms*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100010

int a[N],b[N];
int n,m;

struct node
{
    int stx,sty;
}tree[4*N];

void build(int l,int r,int rt)
{
    tree[rt].stx = tree[rt].sty = 0;
    if(l == r)
    {
        return;
    }
    int mid = (l+r)/2;
    build(l,mid,2*rt);
    build(mid+1,r,2*rt+1);
}

void pushdown(int rt)
{
    if(tree[rt].stx)
    {
        tree[2*rt].stx = tree[2*rt+1].stx = tree[rt].stx;
        tree[2*rt].sty = tree[2*rt+1].sty = tree[rt].sty;
        tree[rt].stx = tree[rt].sty = 0;
    }
}
void change(int l,int r,int aa,int bb,int stx,int sty,int rt)
{
    if(aa<=l&&bb>=r)
    {
        tree[rt].stx = stx;
        tree[rt].sty = sty;
        return;
    }
    pushdown(rt);
    int mid = (l+r)/2;
    if(bb<=mid)
        change(l,mid,aa,bb,stx,sty,2*rt);
    else if(aa>mid)
        change(mid+1,r,aa,bb,stx,sty,2*rt+1);
    else
    {
        change(l,mid,aa,bb,stx,sty,2*rt);
        change(mid+1,r,aa,bb,stx,sty,2*rt+1);
    }
}

node query(int l,int r,int pos,int rt)
{
    if(l == r)
    {
        return tree[rt];
    }
    pushdown(rt);
    int mid = (l+r)/2;
    if(pos<=mid)
        return query(l,mid,pos,2*rt);
    return query(mid+1,r,pos,2*rt+1);
}

int main()
{

    int i;
    int x,y,k,pos;
    int op;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(i=1;i<=n;i++)
        {
            scanf("%d",&b[i]);
        }
        build(1,n,1);
        for(i=0;i<m;i++)
        {
            scanf("%d",&op);
            if(op == 1)
            {
                scanf("%d%d%d",&x,&y,&k);
                change(1,n,y,y+k-1,x,y,1);
            }
            else
            {
                scanf("%d",&pos);
                node ans = query(1,n,pos,1);
                if(ans.stx == 0)
                {
                    printf("%d
",b[pos]);
                }
                else
                {
                    printf("%d
",a[pos-ans.sty+ans.stx]);
                }
            }
        }
    }
    return 0;
}
View Code

E.Circular RMQ ---Codeforces 52C

也是很简单的一道线段树,纯当练手了,还是那样维护,只不过可能换成两个区间查询和加值罢了。。还有值得一提的输入方式,因为比较坑爹的是输入不定,所以借鉴了别人的stringstream 方法,核心代码如下:

gets(buffer);
stringstream ss(buffer);
ss>>aa>>bb;

具体实现在下面。。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <utility>
#include <cstdlib>
#include <sstream>
using namespace std;
#define N 200010

struct node
{
    lll mini;
    lll addmark;
}tree[4*N];

int n,m;
lll a[N];

void pushup(int rt)
{
    tree[rt].mini = min(tree[2*rt].mini,tree[2*rt+1].mini);
}

void build(int l,int r,int rt)
{
    tree[rt].addmark = 0;
    if(l == r)
    {
        tree[rt].mini = a[l];
        return;
    }
    int mid = (l+r)/2;
    build(l,mid,2*rt);
    build(mid+1,r,2*rt+1);
    pushup(rt);
}

void pushdown(int rt)
{
    if(tree[rt].addmark)
    {
        tree[2*rt].mini += tree[rt].addmark;
        tree[2*rt+1].mini += tree[rt].addmark;
        tree[2*rt].addmark += tree[rt].addmark;
        tree[2*rt+1].addmark += tree[rt].addmark;
        tree[rt].addmark = 0;
    }
}

void add(int l,int r,int aa,int bb,int val,int rt)
{
    if(aa>r||bb<l)
        return;
    if(aa<=l&&bb>=r)
    {
        tree[rt].mini += val;
        tree[rt].addmark += val;
        return;
    }
    pushdown(rt);
    int mid = (l+r)/2;
    if(aa<=mid)
        add(l,mid,aa,bb,val,2*rt);
    if(bb>mid)
        add(mid+1,r,aa,bb,val,2*rt+1);
    pushup(rt);
}

lll query(int l,int r,int aa,int bb,int rt)
{    
    if(aa>r||bb<l)
        return (lll)1e17;
    if(aa<=l&&bb>=r)
    {
        return tree[rt].mini;
    }
    pushdown(rt);
    int mid = (l+r)/2;
    if(bb<=mid)
        return query(l,mid,aa,bb,2*rt);
    else if(aa>mid)
        return query(mid+1,r,aa,bb,2*rt+1);
    else
    {
        return min(query(l,mid,aa,bb,2*rt),query(mid+1,r,aa,bb,2*rt+1));
    }
}

char buffer[400000];

int main()
{
    int i,aa,bb;
    lll val,ans;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%I64d",&a[i]);
        }
        build(1,n,1);
        scanf("%d",&m);
        gets(buffer); 
        for(i=0;i<m;i++)
        {
            gets(buffer);
            stringstream ss(buffer);
            ss>>aa>>bb;
            aa++,bb++;
            if(ss>>val)
            {
                if(aa<=bb)
                {
                    add(1,n,aa,bb,val,1);
                }
                else
                {
                    add(1,n,aa,n,val,1);
                    add(1,n,1,bb,val,1);
                }
            }
            else
            {
                if(aa<=bb)
                    ans = query(1,n,aa,bb,1);
                else
                {
                    lll res = query(1,n,aa,n,1);
                    ans = query(1,n,1,bb,1);
                    ans = min(ans,res);
                }
                printf("%I64d
",ans);
            }
        }
    }
    return 0;
}
View Code

H.Vessels ---Codeforces 371D

这道题我原来出的时候记得是线段树,但是后来看别人的代码,没看见一个用线段树写的,,我去。原来硬搞也能过。。难点是想到,。下面放我借鉴别人的代码吧:

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 200010

int cap[N],jp[N],a[N];

int main()
{
    int n,m;
    int i,j;
    int op,pos,val;
    int st;
    while(scanf("%d",&n)!=EOF)
    {
        memset(cap,0,sizeof(cap));
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            jp[i] = i;
        }
        scanf("%d",&m);
        for(i=0;i<m;i++)
        {
            scanf("%d",&op);
            if(op == 1)
            {
                scanf("%d%d",&pos,&val);
                st = jp[pos];
                while(cap[st]+val>=a[st]&&st<=n)
                {
                    val -= (a[st]-cap[st]);
                    cap[st] = a[st];
                    st++;
                }
                if(st<=n)
                    cap[st] += val;
                for(j=jp[pos];j<st;j++)
                {
                    jp[j] = st;
                }
                jp[pos] = st;
            }
            else
            {
                scanf("%d",&pos);
                printf("%d
",cap[pos]);
            }
        }
    }
    return 0;
}
View Code

作者:whatbeg
出处1:http://whatbeg.com/
出处2:http://www.cnblogs.com/whatbeg/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
更多精彩文章抢先看?详见我的独立博客: whatbeg.com

原文地址:https://www.cnblogs.com/whatbeg/p/3492944.html