ACM——线段树

线段树模板:

/*定义*/
const int MAXN=50010;
int a[MAXN],ans[MAXN<<2],lazy[MAXN<<2];
//a[]为原序列信息,ans[]模拟线段树维护区间和,lazy[]为懒惰标记

/*更新结点信息*/
void PushUp(int rt)
{
    ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}

/*建树*/
void Build(int l,int r,int rt)
{
    if (l==r)
    {
        ans[rt]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    Build(l,mid,rt<<1);
    Build(mid+1,r,rt<<1|1);
    PushUp(rt);
}

/*下推懒惰标记*/
void PushDown(int rt,int ln,int rn)//ln表示左子树元素结点个数,rn表示右子树结点个数
{
    if (lazy[rt])
    {
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        ans[rt<<1]+=lazy[rt]*ln;
        ans[rt<<1|1]+=lazy[rt]*rn;
        lazy[rt]=0;
    }
}

/*点更新*/
void Add(int L,int C,int l,int r,int rt)
{
    if (l==r)
    {
        ans[rt]+=C;
        return;
    }
    int mid=(l+r)>>1;
    //PushDown(rt,mid-l+1,r-mid); 若既有点更新又有区间更新,需要这句话
    if (L<=mid)
        Add(L,C,l,mid,rt<<1);
    else
        Add(L,C,mid+1,r,rt<<1|1);
    PushUp(rt);
}

/*区间更新*/
void Update(int L,int R,int C,int l,int r,int rt)
{
    if (L<=l&&r<=R)
    {
        ans[rt]+=C*(r-l+1);
        lazy[rt]+=C;
        return;
    }
    int mid=(l+r)>>1;
    PushDown(rt,mid-l+1,r-mid);
    if (L<=mid) Update(L,R,C,l,mid,rt<<1);
    if (R>mid) Update(L,R,C,mid+1,r,rt<<1|1);
    PushUp(rt);
}

/*区间查询*/
LL Query(int L,int R,int l,int r,int rt)
{
    if (L<=l&&r<=R)
        return ans[rt];
    int mid=(l+r)>>1;
    PushDown(rt,mid-l+1,r-mid);//若更新只有点更新,不需要这句
    LL ANS=0;
    if (L<=mid) ANS+=Query(L,R,l,mid,rt<<1);
    if (R>mid) ANS+=Query(L,R,mid+1,r,rt<<1|1);
    return ANS;
}

/*调用函数*/
    //建树   
    Build(1,n,1);   
    //点更新  
    Add(L,C,1,n,1);  
    //区间修改   
    Update(L,R,C,1,n,1);  
    //区间查询   
    int ANS=Query(L,R,1,n,1);  

1.线段树点更新模板题
题目:
有N个(N<=50000)工兵营地,有N个工兵营地开始时有ai个人(1<=ai<=50)。有以下四种命令:
(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
每组数据最多有40000条命令

#include <iostream>
#include <cstdio>
#include <cstring>
const int MAXN=40010;
const int MAXM=80010;
using namespace std;
struct Edge
{
    int to,next,w;
}e[MAXM];

int vs[2*MAXN],depth[2*MAXN],id[MAXN],dis[MAXN],dmin[2*MAXN][17],cnt;
int edgenum,n,m,head[MAXN];
bool vis[MAXN];
void Add_edge(int u,int v,int w)
{
    e[++edgenum].to=v;
    e[edgenum].w=w;
    e[edgenum].next=head[u];
    head[u]=edgenum;
}
void dfs(int u,int d)///u为当前访问到的结点,d为深度
{
    vis[u]=true;
    vs[++cnt]=u;///vs[]为深度优先访问树的完整路径(包含2*n-1个元素) cnt为时间戳
    id[u]=cnt;///id[u]为u结点首次被访问时的时间戳
    depth[cnt]=d;///depth[u]为u结点的深度
    for (int t=head[u];t!=-1;t=e[t].next)
    {
        int v=e[t].to;
        int w=e[t].w;
        if (!vis[v])
        {
            dis[v]=dis[u]+w;
            dfs(v,d+1);
            vs[++cnt]=u;
            depth[cnt]=d;
        }
    }
}
void RMQ_Init(int n)///dmin[]数组维护的是depth[]最小值对应下标
{
    for (int i=1;i<=n;i++)
        dmin[i][0]=i;
    for (int j=1;(1<<j)<=n;j++)
        for (int i=1;i+(1<<j)-1<=n;i++)
        {
            int a=dmin[i][j-1];
            int b=dmin[i+(1<<(j-1))][j-1];
            if (depth[a]<depth[b])
                dmin[i][j]=a;
            else
                dmin[i][j]=b;
        }
}
int RMQ_min(int L,int R)
{
    int k=0;
    while ((1<<(k+1))<=R-L+1) k++;
    int a=dmin[L][k];
    int b=dmin[R-(1<<k)+1][k];
    if (depth[a]<depth[b])
        return a;
    else
        return b;
}
int LCA(int u,int v)
{
    int x=id[u],y=id[v];
    if (x>y) swap(x,y);
    int res=RMQ_min(x,y);
    return vs[res];
}

int main()
{
    int T,i,u,v,w,x,y;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        edgenum=0;cnt=0;
        memset(head,-1,sizeof(head));
        memset(id,0,sizeof(id));
        memset(vs,0,sizeof(vs));
        memset(dis,0,sizeof(dis));
        memset(depth,0,sizeof(depth));
        memset(vis,false,sizeof(vis));
        for (i=1;i<n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            Add_edge(u,v,w);
            Add_edge(v,u,w);
        }
        dfs(1,0);
        RMQ_Init(2*n-1);
        for (i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",dis[x]+dis[y]-2*dis[LCA(x,y)]);
        }
    }
    return 0;
}

2.线段树 区间更新+离散化 经典染色问题
题目:给定一条数轴,长度为107,然后在数轴上的某些区间染色,第i次对区间染色为i,共染色n(n≤105)次。给出每次染色的区间,问最后能看见多少种颜色.

#include <iostream>
#include <cstring>
#include <algorithm>
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
const int MAXN=10010;
int Tree[MAXN*8],lazy[MAXN*8],d[MAXN*2],SIZE;
//Tree[]用线段树维护颜色信息,lazy[]为懒惰标记,d[]为处理离散化的数组
void Build(int l,int r,int rt)//建树
{
    if (l==r)
    {
        Tree[rt]=0;//颜色值初始为0
        return;
    }
    int mid=(l+r)>>1;
    Build(l,mid,ls);
    Build(mid+1,r,rs);
}
void PushDown(int rt)//下推标记,可以延迟更新
{
    if (lazy[rt])
    {
        lazy[ls]=lazy[rs]=lazy[rt];
        Tree[ls]=lazy[rt];
        Tree[rs]=lazy[rt];
        lazy[rt]=0;//清空标记
    }
}
void Update(int L,int R,int c,int l,int r,int rt)//[L,R]为更新区间,c为要覆盖的颜色值
{
    if (L<=l&&r<=R)
    {
        Tree[rt]=c;
        lazy[rt]=c; //延迟标记
        return;
    }
    int mid=(l+r)>>1;
    PushDown(rt);//要往子树访问时,下推标记
    if (L<=mid) Update(L,R,c,l,mid,ls);
    if (R>mid) Update(L,R,c,mid+1,r,rs);
}
int Query(int x,int l,int r,int rt)//单点查询叶子节点颜色值
{
    if (l==r)
        return Tree[rt];
    int mid=(l+r)>>1;
    PushDown(rt);
    if (x<=mid) return(Query(x,l,mid,ls));
    else if (x>mid) return(Query(x,mid+1,r,rs));
}
void Count()//统计叶子节点有多少个颜色值
{
    int book[MAXN*2],ans=0,i;//book[i]用来标记i号颜色是否出现
    memset(book,0,sizeof(book));
    for (i=1;i<=SIZE;i++)
    {
        int t=Query(i,1,SIZE,1);
        book[t]=1;
    }
    for (i=1;i<=SIZE;i++)
        if (book[i]) ans++;
    cout<<ans<<endl;
}
int main()
{

    ios::sync_with_stdio(false);
    int T,i,n,cnt,e[MAXN*2][3];
    cin>>T;
    while (T--)
    {
        cin>>n;
        cnt=0;
        /*离散化过程*/
        for (i=1;i<=n;i++)
        {
            cin>>e[i][1]>>e[i][2];
            d[++cnt]=e[i][1];
            d[++cnt]=e[i][2];
        }
        sort(d+1,d+cnt+1);
        SIZE=unique(d+1,d+cnt+1)-d-1;

        memset(lazy,0,sizeof(lazy));
        Build(1,SIZE,1);
        for (i=1;i<=n;i++)//用映射后的区间处理问题
        {
            int L=lower_bound(d+1,d+SIZE+1,e[i][1])-d;
            int R=lower_bound(d+1,d+SIZE+1,e[i][2])-d;
            Update(L,R,i,1,SIZE,1);
        }
        Count();
    }
    return 0;
}

参考文章:https://blog.csdn.net/yyt330507870/article/details/70037612

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值