线段树——Segment Tree

24 篇文章 0 订阅
9 篇文章 0 订阅

Segment Tree

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。 —— [ 百度百科 ]

它的工作原理是这样的:

但是这些并不是重点,重点是下面的两道题:

行星序列(seq)

Description:

“神州“载人飞船的发射成功让小可可非常激动,他立志长大后要成为一名宇航员假期一始,他就报名参加了“小小宇航员夏令营”,在这里小可可不仅学到了丰富的宇航知识,还参与解决了一些模拟飞行中发现的问题,今天指导老师交给他一个任务,在这次模拟飞行的路线上有N个行星,暂且称它们为一个行星序列,并将他们从1至n标号,在宇宙未知力量的作用下这N个行星的质量是不断变化的,所以他们对飞船产生的引力也会不断变化,小可可的任务就是在飞行途中计算这个行星序列中某段行星的质量和,以便能及时修正飞船的飞行线路,最终到达目的地,行星序列质量变化有两种形式:
1,行星序列中某一段行星的质量全部乘以一个值
2,行星序列中某一段行星的质量全部加上一个值
由于行星的质量和很大,所以求出某段行星的质量和后只要输出这个值模P的结果即可,小可可被这个任务难住了,聪明的你能够帮他完成这个任务吗?

Input:

第一行两个整数N和P(1<=p<=1000000000);
第二行含有N个非负整数,从左到右依次为a1,a2,…………,an(0<=ai<。100000000,1<=i<=n),其中ai表示第i个行星的质量:
第三行有一个整数m,表示模拟行星质量变化以及求质量和等操作的总次数。从第四行开始每行描述一个操作,输入的操作有以下三种形式:

  • 操作1:1 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai*c
  • 操作2:2 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai+c
  • 操作3:3 t g 表示输出所有满足t<=i<=g的ai的和模p的值

其中:1<=t<=g<=N,0<=c<=10000000
注:同一行相邻的两数之间用一个空格隔开,每行开头和末尾没有多余空格
输出:对每个操作3,按照它在输入中出现的顺序,依次一行输出一个整数表示所求行星质量和

Example:

Input:
7 43
1 2 3 4 5 6 7 
5
1 2 5 5 
3 2 4
2 3 7 9
3 1 3 
3 4 7
Output:
2
35
8

Hint

100%的数据中,M,N<=100000
40%的数据中,M,N<=10000

Solution:

本题和BZOJ1798是一道题,难点就是操作1的实现。
这个时候我们就要用两个标记,一个来执行加法,一个执行乘法:
:维护两个标记mul和add
 标记之间的更新,更新乘法标记时需将之前的加法标记更新
 先下放乘法标记,再下放加法标记
 下放乘法:sum=sum*c, mul=mul*c, add=add*c
 下放加法:sum=sum+c*len, add=add+c
一些细节问题详见Code:

#include <stdio.h>
#include <string.h>
#define MAXN 100000+100
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
ll n,m,p,flag,st,ed,c;
ll a[MAXN],sum[MAXN<<2],add[MAXN<<2],mul[MAXN<<2];
ll max(ll a,ll b){return a>b?a:b;}
void Pushup(ll rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    sum[rt]%=p;
}
void Pushdown(ll rt,ll m)
{
    if(add[rt]||mul[rt]!=1)
    {
        mul[rt<<1]*=mul[rt];                                                    mul[rt<<1]%=p;
        mul[rt<<1|1]*=mul[rt];                                                  mul[rt<<1|1]%=p;
        add[rt<<1]=add[rt<<1]*mul[rt]+add[rt];                                  add[rt<<1]%=p;
        add[rt<<1|1]=add[rt<<1|1]*mul[rt]+add[rt];                              add[rt<<1|1]%=p;
        sum[rt<<1]=sum[rt<<1]*mul[rt]+add[rt]*(m-(m>>1));                       sum[rt<<1]%=p;
        sum[rt<<1|1]=sum[rt<<1|1]*mul[rt]+add[rt]*(m>>1);                       sum[rt<<1|1]%=p;
        add[rt]=0;
        mul[rt]=1;
    }
}
void build(ll l,ll r,ll rt)
{
    add[rt]=0;
    mul[rt]=1;
    if(l==r)
    {
        scanf("%lld",&sum[rt]);
        return ;
    }
    ll mid=(l+r)>>1;
    build(lson);
    build(rson);
    Pushup(rt);
}
void update_add(ll L,ll R,ll c,ll l,ll r,ll rt) 
{
    if(L<=l&&r<=R)
    {
        add[rt]+=c;         add[rt]%=p;
        sum[rt]+=c*(r-l+1); sum[rt]%=p;
        return ;
    }
    Pushdown(rt,r-l+1);
    ll mid=(l+r)>>1;
    if(L<=mid) update_add(L,R,c,lson);
    if(R>mid) update_add(L,R,c,rson);
    Pushup(rt);
}
void update_mul(ll L,ll R,ll c,ll l,ll r,ll rt) 
{
    if(L<=l&&r<=R)
    {
        sum[rt]*=c; sum[rt]%=p;
        mul[rt]*=c; mul[rt]%=p;
        add[rt]*=c; add[rt]%=p;
        return ;
    }
    Pushdown(rt,r-l+1);
    ll mid=(l+r)>>1;
    if(L<=mid) update_mul(L,R,c,lson);
    if(R>mid) update_mul(L,R,c,rson);
    Pushup(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt) 
{
    if(L<=l&&r<=R)  return sum[rt]%p;
    Pushdown(rt,r-l+1);
    ll mid=(l+r)>>1,ret=0;
    if(L<=mid) ret+=query(L,R,lson);    ret%=p; 
    if(R>mid) ret+=query(L,R,rson);     ret%=p;
    return ret;
}
int main()
{
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    scanf("%lld%lld",&n,&p);
    build(1,n,1);
    scanf("%lld",&m);
    while(m--)
    {
        scanf("%lld",&flag);
        if(flag==1)
        {
            scanf("%lld%lld%lld",&st,&ed,&c);
            update_mul(st,ed,c,1,n,1);
        }
        if(flag==2)
        {
            scanf("%lld%lld%lld",&st,&ed,&c);
            update_add(st,ed,c,1,n,1);
        }
        if(flag==3)
        {
            scanf("%lld%lld",&st,&ed);
            printf("%lld\n",query(st,ed,1,n,1)%p);
        }
    }
    return 0;
}

/*

7 48
1 2 3 4 5 6 7
10
1 1 3 5
3 1 3

ans 30
*/

2.借教室续classroom

%@^&#*!

还记得 NOIP 2012 提高组Day2 中的借教室吗?时光飞逝,光阴荏苒,两年
过去了,曾经借教室的同学们纷纷归还自己当初租借的教室。请你来解决类似于
借教室的另一个问题。

Description:

在接受借教室请求的n 天中,第i 天剩余的教室为ai个。作为大学借教室服
务的负责人,你需要完成如下三种操作共m次:
① 第 l 天到第r 天,每天被归还d 个教室。
② 询问第l 天到第r 天教室个数的平均数。
③ 询问第l 天到第r 天教室个数的方差。

Input

第一行包括两个正整数n 和m,其中n 为借教室的天数,m为操作次数。
接下来一行,共包含n个整数,第i 个整数表示第i天剩余教室数目为ai个。
接下来m 行,每行的第一个整数为操作编号(只能为1 或2 或3),接下来
包含两个正整数l 和r,若操作编号为1,则接下来再包含一个正整数d。

Output

对于每个操作2 和操作3,输出一个既约分数(分子与分母互质)表示询问
的答案(详见样例)。若答案为0,请输出“0/1”(不含引号)。

Example.in

5 4
1 2 3 4 5
1 1 2 3
2 2 4
3 2 4
3 1 5

Example.out

4/1
2/3
14/25

Hint

初始情况下,剩余教室数量为(1, 2, 3, 4, 5)。
第 1 次操作为第1 天到第2 天归还3 个教室,变为(4, 5, 3, 4, 5)。
第 2 次操作询问第2 天到第4 天的平均数为(5+3+4)/3 = 4/1
第 3 次操作询问第2 天到第4 天的方差为(1 +1 +0)/3 = 2/3
第4次操作询问第1天到第5天的方差为(0.04 +0.64 +1.44+ 0.04 +0.64)/5= 14/25

Tips:

n 个数的平均数为:
n 个数的方差为:

Level

所有测试点的数据规模如下:

测试点编号n, m的规模约定
11 ≤ n, m ≤ 50不存在操作3
21 ≤ n, m ≤ 100不存在操作3
31 ≤ n, m ≤ 500/
41 ≤ n, m ≤ 1,000/
51 ≤ n, m ≤ 2,500/
61 ≤ n, m ≤ 5,000/
71 ≤ n, m ≤ 50,000不存在操作3
81 ≤ n, m ≤ 60,000不存在操作3
91 ≤ n, m ≤ 80,000不存在操作3
101 ≤ n, m ≤ 100,000不存在操作3
111 ≤ n, m ≤ 60,000不存在操作1
121 ≤ n, m ≤ 60,000不存在操作1
131 ≤ n, m ≤ 70,000对于操作1,满足l = r
141 ≤ n, m ≤ 70,000对于操作1,满足l = r
151 ≤ n, m ≤ 80,000/
161 ≤ n, m ≤ 80,000/
171 ≤ n, m ≤ 90,000/
181 ≤ n, m ≤ 90,000/
191 ≤ n, m ≤ 100,000/
201 ≤ n, m ≤ 100,000/

对于全部测试数据满足:1 ≤ l ≤ r ≤ n,0 ≤ ai ≤ 10,1 ≤ d ≤ 3,操作1 的数量
不超过10%。
注意:ai和d 的范围很小及操作1 数量很少的原因是为了保证答案
的分子不会很大,以防止答案的分子溢出64位整数的范围,这与题目做法无关。

Solution:

方差。。该死的方差。。。
对于操作2来说,如果设a为(st,ed)这一段的区间和,那么平均数为: a/n
对于两个整形的数我们把分子分母同时除以两个数的gcd就可以达到化简的目的了。
但是,根据题中的叙述,我们是追求精度的,所以需要用到浮点型。一般情况下我们会选择用double,为什么?因为它长啊。但是如果把double转化成long long ,或者直接*10000,就会出现6200—>5199的误差,这是绝对不能接受的。所以第一个要点就是需要用float。
但这样的做法也并不完美。
方差除了题目给的算法外,还有另外一种特别神奇的方法:
方差:等于平方和的平均数减去平均数的平方
这样我们就可以通过通分,把分子分母又转化为整形,然后就同上了。
所以,现在就差一点点了。即怎样维护一个平方和?
公式请见:

http://blog.csdn.net/magical_qting/article/details/47210471

Code:

#include<stdio.h>
#include<string.h>
#define MAXN 111111
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
ll n,m,flag,st,ed,c;
ll sum[MAXN<<2],add[MAXN<<2],sum2[MAXN<<2];
ll gcd(ll a,ll b)
{
    if(b==0)
        return a;
    else return gcd(b,a%b);
}
void Pushup(ll rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    sum2[rt]=sum2[rt<<1]+sum2[rt<<1|1];
}
void Pushdown(ll rt,ll m)
{
    if(add[rt])
    {
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum2[rt<<1]+=sum[rt<<1]*2*add[rt]+add[rt]*add[rt]*(m-(m>>1));
        sum[rt<<1]+=add[rt]*(m-(m>>1));
        sum2[rt<<1|1]+=sum[rt<<1|1]*2*add[rt]+add[rt]*add[rt]*(m>>1);
        sum[rt<<1|1]+=add[rt]*(m>>1);
        add[rt]=0;
    }
}
void build(ll l,ll r,ll rt)
{
    add[rt]=0;
    if(l==r)
    {
        ll x;
        scanf("%lld",&x);
        sum[rt]=x;
        sum2[rt]=x*x;
        return ;
    }
    ll mid=(l+r)>>1;
    build(lson);
    build(rson);
    Pushup(rt);
}
void update(ll L,ll R,ll c,ll l,ll r,ll rt)
{
    if(L<=l&&r<=R)
    {
        add[rt]+=c;
        sum2[rt]+=2*c*sum[rt]+(r-l+1)*c*c;
        sum[rt]+=c*(r-l+1);
        return ;
    }
    Pushdown(rt,r-l+1);
    ll mid=(l+r)>>1;
    if(L<=mid) update(L,R,c,lson);
    if(R>mid) update(L,R,c,rson);
    Pushup(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt)
{
    if(L<=l&&r<=R)  return sum[rt];
    Pushdown(rt,r-l+1);
    ll mid=(l+r)>>1,ret=0;
    if(L<=mid) ret+=query(L,R,lson);
    if(R>mid) ret+=query(L,R,rson);
    return ret;
}
ll query2(ll L,ll R,ll l,ll r,ll rt)
{
    if(L<=l&&r<=R)  return sum2[rt];
    Pushdown(rt,r-l+1);
    ll mid=(l+r)>>1,ret=0;
    if(L<=mid) ret+=query2(L,R,lson);
    if(R>mid) ret+=query2(L,R,rson);
    return ret;
}
int main()
{
    freopen("classroom.in","r",stdin);
    //freopen("classroom.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    build(1,n,1);
    while(m--)
    {
        scanf("%lld",&flag);
        if(flag==1)
        {
            scanf("%lld%lld%lld",&st,&ed,&c);
            update(st,ed,c,1,n,1);
        }
        if(flag==2)
        {
            scanf("%lld%lld",&st,&ed);
            ll t=ed-st+1,s=query(st,ed,1,n,1);
            if(t==0||s==0)
            {
                puts("0/1");
                continue;
            }
            ll x=gcd(t,s);
            printf("%lld/%lld\n",s/x,t/x);
        }
        if(flag==3)
        {
            scanf("%lld%lld",&st,&ed);
            ll t=ed-st+1,s=query(st,ed,1,n,1);
            if(t==0||s==0)
            {
                puts("0/1");
                break;
            }
            ll sum2=query2(st,ed,1,n,1);
            ll up=sum2*t-s*s;
            ll down=t*t;
            ll d=gcd(up,down);
            printf("%lld/%lld\n",up/d,down/d);
        }
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值