2016.7.9 线段树(未完成版)

    可能是直到几天前的那天晚上时间不够,今天老师几乎给了我们一整天来刷题,不过......感觉不太妙orz;

    参考资料:

    1.线段树课件(来自buctears;

    2.线段树讲义(未注名

    3.线段树+位总结(http://blog.csdn.net/byijie/article/details/8586499

    4.poj2777题解 (http://blog.csdn.net/wuyanyi/article/details/7032305

    5.线段树专辑(http://blog.sina.com.cn/s/articlelist_1763500727_6_1.html

    从参考资料就可以看出,这次的学习比较艰难;

一.poj 3468 A Simple Problem with Integers

    题意:改变值,查总和

    题目分析:我一开始以为,这种线段树的几个操作都涉及了的(build,pushup,oushdown,update,query)题已经算是超级难的了,刚看完函数代码就写的我万分不自信,但因为喜欢自己思考,我硬生生地自己写了出来,很耗费心力......然而,后来我发现,在线段树里求和,真的太仁慈了......

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
const int MAXN=100005;
int n,m,ll,rr;
long long a[MAXN],ad;
char cl;
struct NODE
{
    long long add,sum;
    int rig,lef;
}node[MAXN*8];
void pushdown(int u)
{
        node[u].sum+=node[u].add*(node[u].rig-node[u].lef+1);
        node[2*u].add+=node[u].add;
        node[2*u+1].add+=node[u].add;
        node[u].add=0; 
}
void pushup(int k)
{
    if(!node[2*k].add&&!node[2*k+1].add)
    node[k].sum=node[2*k].sum+node[2*k+1].sum;
    else if(!node[2*k].add) 
    pushdown(2*k+1);
    else
    pushdown(2*k);
}
void build(int u,int l,int r)
{
    node[u].lef=l;node[u].rig=r;node[u].add=0;
    if(l==r)
    {
        node[u].sum=a[l];//线段!树 
        return;
    }
    else 
    {int mid=(l+r)/2;
    build(2*u,l,mid);
    build(2*u+1,mid+1,r);
    }
    pushup(u);//下先上后; 
}
long long query(int u,long long z,long long y)
{
    if(node[u].add)pushdown(u);
    int mid=(node[u].lef+node[u].rig)/2; 
    if(node[u].lef==z&&node[u].rig==y) return node[u].sum;
    else if(z>mid)return query(2*u+1,z,y);
    else if(y<=mid)return query(2*u,z,y);
    else return query(2*u,z,mid)+query(2*u+1,mid+1,y);
} 
void update(int u,long long zz,long long yy)
{
    long long mid=(node[u].lef+node[u].rig)/2; 
    node[u].sum+=(yy-zz+1)*ad;//+1
    if(node[u].lef==zz&&node[u].rig==yy)
    {
        node[2*u].add+=ad;
        node[2*u+1].add+=ad;
    }
    else if(zz>mid) update(2*u+1,zz,yy);
    else if(yy<=mid) update(2*u,zz,yy);
    else 
    {
     update(2*u,zz,mid);
     update(2*u+1,mid+1,yy);
    }    
}

int main()
{
    freopen("aspi.in","r",stdin);
    freopen("aspi.out","w",stdout);
    ios::sync_with_stdio(false);
    cin>>m>>n;//a,q
    for(int i=1;i<=m;i++)
    //scanf("%I64d",&a[i]);
    cin>>a[i];
    build(1,1,m);
    for(int i=1;i<=n;i++)
    {
        cin>>cl;
        switch (cl)
        {
        case 'Q':
        {
            cin>>ll>>rr;
            cout<<query(1,ll,rr)<<endl;
            break;
        }
        case 'C':
        {
            cin>>ll>>rr>>ad;
            update(1,ll,rr);
            break;
        }
        default:break;
        }
    }
    return 0;
}

一般节点开四倍,我八倍才过是因为在update和pushdown里没有考虑叶子节点,即应分别改成

void pushdown(int u)
{
        node[u].sum+=node[u].add*(node[u].rig-node[u].lef+1);
        if(node[u].lef!=node[u].rig)
        {
        node[2*u].add+=node[u].add;
        node[2*u+1].add+=node[u].add;
        }
        node[u].add=0; 
}
oid update(int u,long long zz,long long yy)
{
    long long mid=(node[u].lef+node[u].rig)/2; 
    node[u].sum+=(yy-zz+1)*ad;//+1
    if(node[u].lef!=node[u].rig)
    {
    if(node[u].lef==zz&&node[u].rig==yy)
    {
        node[2*u].add+=ad;
        node[2*u+1].add+=ad;
    }
    else if(zz>mid) update(2*u+1,zz,yy);
    else if(yy<=mid) update(2*u,zz,yy);
    else 
    {
     update(2*u,zz,mid);
     update(2*u+1,mid+1,yy);
    }
    }
}

顺便,通过这次改错我学会了把其他只有几个模板之一的程序提交上去评测orz

特意强调“线段”树是因为我原来居然把lef,rig存成了左右子树节点......mdzz(手动再见

二.I hate it  HDOJ1756

 题意:单个改变,求最大

题目分析:除了坑爹的多数据输出害我改了很久以外,这是最没难度的,这道题开四倍也过了

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
const int MAXN=200005;
int a[MAXN],m,n,l,r;
char cl;
struct STU
{
    int lef,rig,maxn;
}stu[MAXN*4];
void pushup(int u)
{
    stu[u].maxn=max(stu[2*u].maxn,stu[2*u+1].maxn);
}
void build(int u,int l,int r)
{
    stu[u].lef=l;stu[u].rig=r;
    int mid=(l+r)/2;
    if(l==r)
        {
        stu[u].maxn=a[l];
        return;
        }
    else 
    {
        build(2*u,l,mid);
        build(2*u+1,mid+1,r);
        pushup(u);
    }
}
int query(int u,int l,int r)
{
    int mid=(stu[u].lef+stu[u].rig)/2;
    if(stu[u].lef==l&&stu[u].rig==r)return stu[u].maxn;
    else if(l>mid)return query(2*u+1,l,r);
    else if(r<=mid)return query(2*u,l,r);
    else return max(query(2*u,l,mid),query(2*u+1,mid+1,r));
}
void update(int u,int lookfor,int b)
{
    int mid=(stu[u].lef+stu[u].rig)/2;
    if(stu[u].lef==stu[u].rig&&stu[u].lef==lookfor)
    {
        stu[u].maxn=b;return;
    }
    else if(lookfor<=mid)update(2*u,lookfor,b);
    else update(2*u+1,lookfor,b);
    pushup(u);
}
int main()
{
    freopen("hate.in","r",stdin);
    freopen("hate.out","w",stdout);
    ios::sync_with_stdio(false);
    while(cin>>n>>m)
    {
    for(int i=1;i<=n;i++)
    cin>>a[i];
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        cin>>cl;
        switch (cl)
        {
        case 'Q':
        {
            cin>>l>>r;
            cout<<query(1,l,r)<<endl;
            break;
        }
        case 'U':
            {
                cin>>l>>r;
                a[l]=r;
                update(1,l,r);
                break;
            }
        default: break;
        }
    }
    }
    return 0;
}

三.lineup POJ3264

题意:不修改,找最大差距

题目分析:这道题做起来没有刚才那么得心应手,还是好好想了一下,因为不需要更改,这道题没有用到update和pushdown两个函数,想到用tallest和shortest来表示差异是关键;

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
const int MAXN=50005;
int h[MAXN],n,t,s,q,l,r;
struct COW
{
    int tallest,shortest,lef,rig;
}cow[MAXN*4];
void pushup(int u)
{
    cow[u].tallest=max(cow[2*u].tallest,cow[2*u+1].tallest);
    cow[u].shortest=min(cow[2*u].shortest,cow[2*u+1].shortest);
}
void build(int u,int l,int r)
{
    cow[u].lef=l;cow[u].rig=r;
    if(l==r)
    {
        cow[u].tallest=cow[u].shortest=h[l];
    }
    else
    {
        int mid=(l+r)/2;
        build(2*u,l,mid);
        build(2*u+1,mid+1,r);
        pushup(u);
    }
}
void query(int u,int l,int r)
{
    int mid=(cow[u].lef+cow[u].rig)/2;
    if(cow[u].lef==l&&cow[u].rig==r)
    {
        t=max(t,cow[u].tallest);
        s=min(s,cow[u].shortest);
    }
    else if(l>mid) query(2*u+1,l,r);
    else if(r<=mid) query(2*u,l,r);
    else 
    {
        query(2*u,l,mid);query(2*u+1,mid+1,r);
    }
}
int main()
{
    freopen("lineup.in","r",stdin);
    freopen("lineup.out","w",stdout);
    ios::sync_with_stdio(false);
    cin>>n>>q;
    for(int i=1;i<=n;i++)
    cin>>h[i];
    build(1,1,n);
    for(int i=1;i<=q;i++)
    {
        t=0;s=2e9;
        cin>>l>>r;
        query(1,l,r);
        cout<<t-s<<endl;
    }
    return 0;
}

四.count color poj2777

题意:成段修改颜色,求颜色和

题目分析:这是遇到的第一道可怕的题!思考了基本方法以后我开始写,然后方法太复杂了基本没动力改,最后借助了题解......

    很多题解都提到这是理解线段树的一道好题,值得写的程度很高,个人觉得比较难,自己写不出来的话可以好好咀嚼一下别人的程序(嘿!说你呢!快吐出来天辣夭寿啦)

    

 
原文地址:https://www.cnblogs.com/SindarDawn/p/5664299.html