线段树专题

来源:互联网 发布:网易uu加速器mac 编辑:程序博客网 时间:2024/04/30 12:39

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] ] 进行更新就可以了,把该区间的任务替换为新任务。


只要把树映射到线段区间上,就好操作了,建立线段树对其操作,区间更新,查询单点值,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;}


 



 

0 0
原创粉丝点击