Codevs 1082 线段树练习 3(线段树分块)

2023-12-25 16:38
文章标签 练习 1082 线段 codevs 分块

本文主要是介绍Codevs 1082 线段树练习 3(线段树分块),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1082 线段树练习 3
时间限制: 3 s
空间限制: 128000 KB
题目等级 : 大师 Maste
传送门
题目描述 Description
给你N个数,有两种操作:
1:给区间[a,b]的所有数增加X
2:询问区间[a,b]的数的和。
输入描述 Input Description
第一行一个正整数n,接下来n行n个整数,
再接下来一个正整数Q,每行表示操作的个数,
如果第一个数是1,后接3个正整数,
表示在区间[a,b]内每个数增加X,如果是2,
表示操作2询问区间[a,b]的和是多少。
输出描述 Output Description
对于每个询问输出一行一个答案
样例输入 Sample Input
3
1
2
3
2
1 2 3 2
2 2 3
样例输出 Sample Output
9
数据范围及提示 Data Size & Hint
数据范围
1<=n<=200000
1<=q<=200000

/*
裸线段树.
区间修改+区间查询.
*/
#include<iostream>
#include<cstdio>
#define MAXN 200001
#define ll long long
using namespace std;
ll n,m,tot,cut,aa[MAXN+10];
struct data
{int l,r;int lc,rc;ll sum;ll bj;
}tree[MAXN*4];
void build(int l,int r)
{int k=++cut;tree[k].l=l;tree[k].r=r;if(l==r){tree[k].sum=aa[l];return ;}int mid=(l+r)>>1;tree[k].lc=cut+1;build(l,mid);tree[k].rc=cut+1;build(mid+1,r);tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
}
void updata(int k)
{tree[tree[k].lc].sum+=tree[k].bj*(tree[tree[k].lc].r-tree[tree[k].lc].l+1);tree[tree[k].rc].sum+=tree[k].bj*(tree[tree[k].rc].r-tree[tree[k].rc].l+1);tree[tree[k].lc].bj+=tree[k].bj;tree[tree[k].rc].bj+=tree[k].bj;tree[k].bj=0;
}
void add(int k,int l,int r,int add1)
{if(l<=tree[k].l&&tree[k].r<=r){tree[k].bj+=add1;tree[k].sum+=add1*(tree[k].r-tree[k].l+1);return ;}if(tree[k].bj) updata(k);int mid=(tree[k].l+tree[k].r)>>1;if(l<=mid) add(tree[k].lc,l,r,add1);if(r>mid) add(tree[k].rc,l,r,add1);tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
}
ll query(int k,int l,int r)
{if(l<=tree[k].l&&tree[k].r<=r) return tree[k].sum;ll tot=0;if(tree[k].bj) updata(k);int mid=(tree[k].l+tree[k].r)>>1;if(l<=mid) tot+=query(tree[k].lc,l,r);if(r>mid) tot+=query(tree[k].rc,l,r);tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;return tot;
}
int main()
{int x,a,b,add1;cin>>n;for(int i=1;i<=n;i++){cin>>aa[i];}build(1,n);cin>>m;for(int i=1;i<=m;i++){scanf("%d",&x);if(x==1){scanf("%d %d %d",&a,&b,&add1);add(1,a,b,add1);}else{scanf("%d %d",&a,&b);printf("%lld\n",query(1,a,b));}}return 0;   
}
/*
分块.
区间修改 区间查询.
这题并没有1A orz.
error:不完整的块修改忘记维护sum了 然后这题要long long.
修改贡献为addval*size.(size不一定是m,可能不完整 其实也无所谓... 
*/
#include<cstdio>
#include<cmath>
#include<iostream>
#define MAXN 200001
#define LL long long
using namespace std;
int n,m,q,s[MAXN],belong[MAXN],sum[MAXN],tag[MAXN],size[MAXN];
LL ans;
int read()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();return x*f;
}
void slovechange(int x,int y,int z)
{for(int i=x;i<=min(y,belong[x]*m);i++) s[i]+=z,sum[belong[i]]+=z;for(int i=belong[x]+1;i<=belong[y]-1;i++) sum[i]+=z*size[i],tag[i]+=z;if(belong[x]!=belong[y])for(int i=(belong[y]-1)*m+1;i<=y;i++) s[i]+=z,sum[belong[i]]+=z;return ;
}
LL slovequery(int x,int y)
{ans=0;for(int i=x;i<=min(y,belong[x]*m);i++) ans+=s[i]+tag[belong[i]];for(int i=belong[x]+1;i<=belong[y]-1;i++) ans+=sum[i];if(belong[x]!=belong[y])for(int i=(belong[y]-1)*m+1;i<=y;i++) ans+=s[i]+tag[belong[i]];return ans;
}
int main()
{int x,y,z,k;n=read();m=sqrt(n);for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1,size[belong[i]]=m;size[belong[n]]=n-(belong[n-1]*size[belong[n-1]]);for(int i=1;i<=n;i++) s[i]=read(),sum[belong[i]]+=s[i];q=read();while(q--){z=read();if(z&1) x=read(),y=read(),z=read(),slovechange(x,y,z);else x=read(),y=read(),printf("%lld\n",slovequery(x,y));}return 0;
}

这篇关于Codevs 1082 线段树练习 3(线段树分块)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/536191

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

RabbitMQ练习(AMQP 0-9-1 Overview)

1、What is AMQP 0-9-1 AMQP 0-9-1(高级消息队列协议)是一种网络协议,它允许遵从该协议的客户端(Publisher或者Consumer)应用程序与遵从该协议的消息中间件代理(Broker,如RabbitMQ)进行通信。 AMQP 0-9-1模型的核心概念包括消息发布者(producers/publisher)、消息(messages)、交换机(exchanges)、

圆与线段的交点

poj 3819  给出一条线段的两个端点,再给出n个圆,求出这条线段被所有圆覆盖的部分占了整条线段的百分比。 圆与线段的交点 : 向量AB 的参数方程  P = A + t * (B - A)      0<=t<=1 ; 将点带入圆的方程即可。  注意: 有交点 0 <= t <= 1 ; 此题求覆盖的部分。 则 若求得 t  满足 ; double ask(d