ACM常用模板——数据结构——线段树

(一)单点更新
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
using namespace std;
const int maxn=222222;
int Max[maxn<<2];
void PushUp(int rt)    //向上更新
{
    Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
}
void build(int l,int r,int rt)  // 建立线段树
{
    if(l==r)
    {
        scanf("%d",&Max[rt]);
        return ;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
void update(int p,int v,int l,int r,int rt)  //单点更新
{
    if(l==r)
    {
        Max[rt]=v;
        return ;
    }
    int m=(l+r)>>1;
    if(p<=m) update(p,v,lson);
    else update(p,v,rson);
    PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)  // 区间最大值查询
{
    if(L<=l&&r<=R)
    {
        return Max[rt];
    }
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m) ans=max(ans,query(L,R,lson));
    if(R>m) ans=max(ans,query(L,R,rson));
    return ans;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        for(int i=0;i<m;i++)
        {
            char c[15];
            int x,y;
            scanf("%s%d%d",c,&x,&y);
            if(c[0]=='Q') printf("%d\n",query(x,y,1,n,1));
            else update(x,y,1,n,1);
        }
    }
    return 0;
}


题目大意:给你n个数。这n个数是0~n-1的某个排列。现在叫你求这n个数的逆序数,以及循环左移后的逆序数的最小值。例如,给你0,3,2,1

叫你求,03,2,1的逆序数,3,2,1,0的逆序数,2,1,0,3的逆序数,1,0,3,2的逆序数 中最小的逆序数是什么。

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
using namespace std;
const int maxn=5005;
int sum[maxn<<2],x[maxn];
void PushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    sum[rt]=0;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
void update(int p,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]++;
        return ;
    }
    int m=(l+r)>>1;
    if(p<=m) update(p,lson);
    else update(p,rson);
    PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        return sum[rt];
    }
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m) ans+=query(L,R,lson);
    if(R>m) ans+=query(L,R,rson);
    return ans;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        build(0,n-1,1);
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x[i]);
            sum+=query(x[i]+1,n-1,0,n-1,1);
            update(x[i],0,n-1,1);
        }
        int ans=sum;
        for(int i=0;i<n;i++)
        {
            sum+=n-1-x[i]-x[i];
            ans=min(ans,sum);
        }
        printf("%d\n",ans);
    }
    return 0;
}


(二)成段更新

update:成段增减 query:区间求和
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
using namespace std;
const int maxn=100005;
LL add[maxn<<2],sum[maxn<<2];
void PushUp(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m)
{
    if(add[rt])
    {
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=(m-(m>>1))*add[rt];
        sum[rt<<1|1]+=(m>>1)*add[rt];
        add[rt]=0;
    }
}
void build(int l,int r,int rt)
{
    add[rt]=0;
    if(l==r)
    {
        scanf("%lld",&sum[rt]);
        return ;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
void update(int L,int R,int x,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        sum[rt]+=(LL)((r-l+1)*x);
        add[rt]+=(LL)x;
        return ;
    }
    PushDown(rt,r-l+1);
    int m=(l+r)>>1;
    if(L<=m) update(L,R,x,lson);
    if(R>m) update(L,R,x,rson);
    PushUp(rt);
}
LL query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        return sum[rt];
    }
    PushDown(rt,r-l+1);
    int m=(l+r)>>1;
    LL ans=0;
    if(L<=m) ans+=query(L,R,lson);
    if(R>m) ans+=query(L,R,rson);
    return ans;
}
int main()
{
    int n,m,x,y,v;
    char op[5];
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        for(int i=0;i<m;i++)
        {
            scanf("%s",op);
            if(op[0]=='Q') scanf("%d%d",&x,&y),printf("%lld\n",query(x,y,1,n,1));
            else scanf("%d%d%d",&x,&y,&v),update(x,y,v,1,n,1);
        }
    }
    return 0;
}

(三)区间合并
区间合并 单点更新
模型:给出一个序列,两种操作,单点更新值,以及查询区间的最长连续递增子序列长度
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
using namespace std;
const int maxn=100005,inf=1<<29;
int lsum[maxn<<2],rsum[maxn<<2],msum[maxn<<2],ln[maxn<<2],rn[maxn<<2];
void PushUp(int rt,int m)//利用子节点信息更新父节点
{
lsum[rt]=lsum[rt<<1];//根节点从左边其的最长区间至少等于左儿子从左边起得最长区间
rsum[rt]=rsum[rt<<1|1];
msum[rt]=max(msum[rt<<1],msum[rt<<1|1]);
ln[rt]=ln[rt<<1];
rn[rt]=rn[rt<<1|1];
if(rn[rt<<1]<ln[rt<<1|1])//左儿子区间右端点比右儿子区间左端点小
{
if(lsum[rt]==m-(m>>1)) lsum[rt]+=lsum[rt<<1|1];
if(rsum[rt]==(m>>1)) rsum[rt]+=rsum[rt<<1];
msum[rt]=max(msum[rt],rsum[rt<<1]+lsum[rt<<1|1]);
}
}
void build(int l,int r,int rt)//建树
{
if(l==r)
{
scanf("%d",&ln[rt]);
//printf("[%d,%d] = %d\n",l,r,ln[rt]);
rn[rt]=ln[rt];
lsum[rt]=rsum[rt]=msum[rt]=1;
return ;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUp(rt,r-l+1);
//printf("msum[%d,%d] = %d\n",l,r,msum[rt]);
}
void update(int p,int x,int l,int r,int rt)//单点更新
{
if(l==r)
{
lsum[rt]=rsum[rt]=msum[rt]=1;
ln[rt]=rn[rt]=x;
return ;
}
int m=(l+r)>>1;
if(p<=m) update(p,x,lson);
else update(p,x,rson);
PushUp(rt,r-l+1);
}
int Query(int L,int R,int l,int r,int rt)
{
//区间是叶子节点或都可用或都不可用直接返回
if(L<=l&&r<=R) return msum[rt];
//if(l==r||msum[rt]==0||msum[rt]==r-l+1) return msum[rt];
int ans=0;
int m=(l+r)>>1;
if(L<=m) ans=max(ans,Query(L,R,lson));
if(R>m) ans=max(ans,Query(L,R,rson));
if(rn[rt<<1]<ln[rt<<1|1]) ans=max(ans,min(rsum[rt<<1],m-L+1)+min(lsum[rt<<1|1],R-m));//不能越了区间[L,R]的界
//printf("msum[%d,%d] = %d ans=%d\n",l,r,msum[rt],ans);
return ans;
//return max(ans,rsum[rt<<1]+lsum[rt<<1|1]);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
//srand(time(NULL));
int n,m,x,y,t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
char op[5];
build(1,n,1);
for(int i=0;i<m;i++)
{
scanf("%s%d%d",op,&x,&y);
if(op[0]=='U') update(x+1,y,1,n,1);
else printf("%d\n",Query(x+1,y+1,1,n,1));
}
}
return 0;
}

阅读更多
文章标签: acm 算法 数据结构
个人分类: acm
上一篇ACM常用模板——数据结构——树状数组
下一篇[Android]toolbar的定义和应用
想对作者说点什么? 我来说一句

线段树 树状数组 数据结构

2011年07月24日 1.29MB 下载

ACM常用模板 ACM常用模板

2010年07月04日 79KB 下载

ACM 算法 常用模板

2010年07月28日 280B 下载

ACM之java速成

2013年12月13日 52KB 下载

线段树和树状数组入门介绍

2010年08月11日 155KB 下载

线段树代码

2014年09月11日 26KB 下载

acm程序设计竞赛_培训_线段树

2011年06月10日 705KB 下载

没有更多推荐了,返回首页

关闭
关闭