线段树

11 篇文章 1 订阅
6 篇文章 0 订阅

线段树

线段树可用于查询修改各种区间问题,它在信息学竞赛中的使用无比广泛,从普及到 CTSC 到处都有它的身影。

模板

[Luogu3372]【模板】线段树 1

已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上x;
  2. 求出某区间每一个数的和.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int MAXN=1e5+5,MAXM=1e5+5;
typedef long long ll;
int n,m;

ll na[MAXN];

const int rt=1;
struct SEGTN{int l,r,lc,rc;ll sum,tag;} t[MAXN<<1];int tcnt=rt;
void upd(int o){t[o].sum=t[t[o].lc].sum+t[t[o].rc].sum;}
void pushdown(int o)
{
    int lc=t[o].lc,rc=t[o].rc;
    if(t[o].tag) 
    {
        t[lc].tag+=t[o].tag;t[lc].sum+=(t[lc].r-t[lc].l+1)*t[o].tag;
        t[rc].tag+=t[o].tag;t[rc].sum+=(t[rc].r-t[rc].l+1)*t[o].tag;
        t[o].tag=0;
    }    
}
void buildTree(int o,int l,int r)
{
    t[o].l=l,t[o].r=r;
    if(l==r)
    {
        t[o].sum=na[l];return;
    }
    int mid=(l+r)>>1;
    t[o].lc=++tcnt;buildTree(t[o].lc,l,mid);
    t[o].rc=++tcnt;buildTree(t[o].rc,mid+1,r);
    upd(o);
}
void chDet(int o,int l,int r,ll v)
{
    if(l<=t[o].l&&t[o].r<=r) 
    {
        t[o].tag+=v,t[o].sum+=(t[o].r-t[o].l+1)*v;
        return;
    }
    pushdown(o);
    int mid=(t[o].l+t[o].r)>>1;
    if(l<=mid) chDet(t[o].lc,l,r,v);
    if(r>mid) chDet(t[o].rc,l,r,v);
    upd(o);
}
ll calSum(int o,int l,int r)
{
    if(l<=t[o].l&&t[o].r<=r) return t[o].sum;
    int mid=(t[o].l+t[o].r)>>1;
    pushdown(o);
    ll res=0;
    if(l<=mid) res+=calSum(t[o].lc,l,r);
    if(r>mid) res+=calSum(t[o].rc,l,r);
    return res;
}
int main()
{
    int i;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&na[i]);
    buildTree(rt,1,n);
    while(m--)
    {
        int opt,x,y;scanf("%d%d%d",&opt,&x,&y);
        if(opt==1)
        {
            ll z;scanf("%lld",&z);
            chDet(rt,x,y,z);
        }else printf("%lld\n",calSum(rt,x,y));
    }
}

拓展应用

区间取模

由于一个数取模一次其大小最大是原来的 12 ,所以一直对一个数 n 取模的话最多只需 log2n 次就会被模成 0 .

那我们维护区间最大值,对区间取模时判断区间最大值是否小于要模的值。若是,则不用操作,否则暴力更新即可。

[ZOJ3886]Nico Number

如果正整数 x 满足不大于 x 且与 x 互质的数能组成一个等差数列,那么我们称 x 为 Niconico 数。

现在给出一个长度 N 的数列,有如下 T 个操作:

  1. 统计一个区间内 Niconico 数的个数;
  2. 区间取模;
  3. 单点修改。

N,T105,107

其实这道题的难点是 Niconico 数
Niconico 数一定是:质数或 2k 6 .

神奇的标记

[2017.07.18] 心理学概论

给定 N 个三元组 (xi,yi,zi) ,现在有三个集合 X,Y,Z ,每个三元组可以取 xi,yi,zi 中的任意一个值并将这个值并放入对应的 X,Y,Z 集合中,求这三个集合中元素的最大值之和的最小值。

N105,1xi,yi,zi108

首先我们考虑二元组的情况,我们可以先将二元组按照 xi 的升序排序,然后我们可以枚举一个位置 p ,取 xi,i[1,p] yj,j[p+1,N] ,由于 xp 是选中的 x 中最大的,这样答案就是 xp+Max{yi,i[p+1,N]}

那么我们考虑三元组的情况,我们第一想法就是将三元组再按 yi 的升序排序,然后再枚举一遍,这样时间复杂度就是 O(N2) ,只能通过 50% 的数据。

那么我们能优化的就是枚举第二个边界的时间复杂度了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值