码迷,mamicode.com
首页 > 其他好文 > 详细

A Simple Problem with Integers

时间:2020-02-11 19:07:08      阅读:53      评论:0      收藏:0      [点我收藏+]

标签:变量   解法   还需   解答   操作   售票   UNC   参数   铁路局   

这是一道简单应用线段树的题

代码也是书上的,敲一边熟悉一下

#include <iostream>
#include <cstdio>
using namespace std;
const int MAX=1e5+10;
long long sum[MAX<<2],add[MAX<<2];
void up_replace(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int rt,int _size){
if(add[rt]){
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
sum[rt<<1]+=add[rt]*(_size-(_size>>1));
sum[rt<<1|1]+=add[rt]*(_size>>1);
add[rt]=0;
}
}
void build(int left,int right,int root){
add[root]=0;
if(left==right){
scanf("%lld",&sum[root]);
return;
}
int mid=(left+right)>>1;
build(left,mid,root<<1);
build(mid+1,right,root<<1|1);
up_replace(root);
}
void update(int a,int b,long long c,int l,int r,int root){
if(a<=l&&b>=r){
sum[root]+=(r-l+1)*c;
add[root]+=c;
return;
}
push_down(root,(r-l+1));
int mid=(l+r)>>1;
if(a<=mid){
update(a,b,c,l,mid,root<<1);
}
if(b>mid){
update(a,b,c,mid+1,r,root<<1|1);
}
up_replace(root);
}
long long query(int a,int b,int l,int r,int rt){
if(a<=l&&b>=r)return sum[rt];
push_down(rt,(r-l+1));
int mid=(l+r)>>1;
long long ans=0;
if(a<=mid){
ans+=query(a,b,l,mid,rt<<1);
}
if(b>mid){
ans+=query(a,b,mid+1,r,rt<<1|1);
}
return ans;
}
int main()
{
int n,m,a,b;
long long c;
scanf("%d %d",&n,&m);
build(1,n,1);
while(m--){
char ch[2];
scanf("%s",ch);
if(ch[0]==‘C‘){
scanf("%d %d %lld",&a,&b,&c);
update(a,b,c,1,n,1);
}
else if(ch[0]==‘Q‘){
scanf("%d %d",&a,&b);
printf("%lld\n",query(a,b,1,n,1));
}
}
return 0;
}
//不分<<和>>,不分l,mid,r,aaaaaaa
//输入流和输出流
//int不行,必须得long long

接下来这些是借鉴网上大佬的总结

线段树的应用有:

(1)  有一列数,初始值全部为零0.每次可以进行如下的三种操作中的一种,

a,         在给定区间的每个数上加上一个特定的值;

b,        将指定区间的所有数设置成一个统一的值;

c,         询问一个区间上的最小值,最大值,以及区间的所有树的和。

这是一个典型的线段树的应用,在每个节点上维护一下的变量,delta(区间增加值),same(区间统一设置为某个值),min(区间的最小值),max(区间的最大值),sum(区间和)。其中的delta和same属于“延迟标记”。

在这里面,给出自己的伪代码算法:当然采用的是“延时标记”的算法。

主要是针对区间修改时的算法,记住“延时标记”的算法在涉及到区间查询时仍然需要下移操作。并且伪代码中建树的过程忽略:

返回区间查询所需要的数值(可能是一个容器) function 对于节点v,查询区间[x,y]

    如果节点v所表示的区间与[x,y]有交集。

        if(v所表示的区间完全属于[x,y])

        {

            对v区间的最大值,最小值,以及区间的和进行修改。

            并且增加对v的标记。

            返回区间查询所需要的数值。

        }

        Push_down()                    // 将标示信息移动到其左右孩子节点中。

        else  递归访问v的左儿子;

   否则,递归访问v的左右儿子;

   update v节点自身的查询时所需要的值。

end function

 

(2)  在所有不大于30000的自然数范围内讨论一个问题,已知n条线段,把端点依次输入给你,然后有m(<=30000)个询问,每个询问输入一个点,要求这个点在多少天线段上出现过,这个题也是线段树的一个很典型的应用。

解法是:首先我们要建立起来30000区间的线段树,在每一个线段树的节点里面,维持一个变量,叫做cnt。然后将n条线段分别插入到建立起来的线段树中,利用区间查询,每一个查询到节点的cnt值加上1.然后查询某个树的时候,直接把这个树从根节点到叶节点的所有的cnt加在一起就可以了。

(3)  某列车途径C个城市,城市编号分别为1到C,列车上共有S个座位,铁路局规定售出的票只能是坐票(貌似与现实不太符合啊,呵呵),即车上的所有旅客都有坐。售票系统是由计算机执行的。每个售票申请包含3个参数,分别用O,D,N表示,O为起始点,D为目标节点,N为票数,售票系统对这次申请做出受理或不受理的回答。只要当有O到D一段的座位>=N的时候,才受理请求。

解答:我们可以把所有的车站放置数轴上,在每个节点维护两个值,delta和剩余值,然后依次对售票请求进行插入,进行区间修改和查询,没插入一请求,相应得给这个点的delta设置成标示位。查询的时候,就找所有区间中的最大值。看看与N来进行比较。

(4)  有一条直线,每次把其中的一段染成某种颜色,要求输出最终的染色情况。

解答:这道题,其实我们将直线离散化,然后进行标记。如果一段区间被染上了相同的颜色,那么只要在节点上做一个标记即可,这表明这个区间现在被染上了标记所代表的颜色。

用区间查询的算法将所构成的O(logn)个区间分别进行标记即可。如果一个区间的子区间要被标记成另一种颜色,那么需要将本区间的比较“下传”,然后再将本节点的标记取消。

但是还需要注意的一点问题是,比方说如果[0,1]区间的两个子节点此时颜色不同,且[0,1]这个时候没有标记位,如果某一次的修改将[0,0]与[1,1]修改成了相同的颜色,那么这个时候为了更加快速的给与判断,我们要回来将[0,1]区间的标记加上。即如果左右子节点都被标记了并且标记相同的话,那么需要再一次更新父节点的标记。

 

A Simple Problem with Integers

标签:变量   解法   还需   解答   操作   售票   UNC   参数   铁路局   

原文地址:https://www.cnblogs.com/sos3210/p/12296037.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!