Codevs 1080 线段树练习(线段树树状数组分块CDQ分治)

2023-12-25 16:38

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

1080 线段树练习
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 钻石 Diamond
传送门
题目描述 Description
一行N个方格,开始每个格子里都有一个整数。现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和;修改的规则是指定某一个格子x,加上或者减去一个特定的值A。现在要求你能对每个提问作出正确的回答。1≤N<100000,,提问和修改的总数m<10000条。
输入描述 Input Description
输入文件第一行为一个整数N,接下来是n行n个整数,表示格子中原来的整数。接下一个正整数m,再接下来有m行,表示m个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和。
输出描述 Output Description
共m行,每个整数
样例输入 Sample Input
6
4
5
6
2
1
3
4
1 3 5
2 1 4
1 1 9
2 2 6
样例输出 Sample Output
22
22
数据范围及提示 Data Size & Hint
1≤N≤100000, m≤10000 。

/*
最裸线段树(闭区间版). 
支持单点修改+区间求和.
*/
#include<iostream>
#include<cstdio>
#define MAXN 100001
using namespace std;
struct data
{int r,l;int rc,lc;int sum;int bj;int tot;
}
tree[MAXN*4];
int n,m,cut,aa[MAXN+10];
void bluid(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;bluid(l,mid);tree[k].rc=cut+1;bluid(mid+1,r);tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
}
void add(int k,int x,int add1)//单点修改 
{if(tree[k].l==tree[k].r) tree[k].sum+=add1;else{int mid=(tree[k].l+tree[k].r)>>1;if(x<=mid) add(tree[k].lc,x,add1);if(x>mid) add(tree[k].rc,x,add1);tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;}
}
int query(int k,int ll,int rr)//区间求和 
{if(ll<=tree[k].l&&tree[k].r<=rr){return tree[k].sum;}int tot=0;int mid=(tree[k].l+tree[k].r)>>1;if(ll<=mid) tot+=query(tree[k].lc,ll,rr);if(rr>mid) tot+=query(tree[k].rc,ll,rr);return tot;
}
int main()
{int x,a,add1,b;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&aa[i]);}bluid(1,n);scanf("%d",&m);for(int i=1;i<=m;i++){scanf("%d",&x);if(x==1){scanf("%d %d",&a,&add1);add(1,a,add1);}else{scanf("%d %d",&a,&b);printf("%d\n",query(1,a,b));}}return 0;
}
/*
最裸线段树(开区间版). 
支持单点修改+区间求和.
*/
#include<iostream>
#include<cstdio>
#define MAXN 100001
using namespace std;
struct data
{int r,l;int rc,lc;int sum;int bj;int tot;
}
tree[MAXN*4];
int n,m,cut,aa[MAXN+10];
void bluid(int l,int r)//建树 
{int k=++cut;tree[k].l=l;tree[k].r=r;if(l==r-1){tree[k].sum=aa[l];return ;}int mid=(l+r)>>1;tree[k].lc=cut+1;bluid(l,mid);tree[k].rc=cut+1;bluid(mid,r);tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
}
void add(int k,int x,int add1)//单点修改 
{if(tree[k].l==tree[k].r-1) tree[k].sum+=add1;else{int mid=(tree[k].l+tree[k].r)>>1;if(x<mid) add(tree[k].lc,x,add1);if(x>=mid) add(tree[k].rc,x,add1);tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;}
}
int query(int k,int ll,int rr)//区间求和 
{if(ll<=tree[k].l&&tree[k].r<=rr){return tree[k].sum;}int tot=0;int mid=(tree[k].l+tree[k].r)>>1;if(ll<mid) tot+=query(tree[k].lc,ll,rr);if(rr>mid) tot+=query(tree[k].rc,ll,rr);return tot;
}
int main()
{int x,a,add1,b;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&aa[i]);}bluid(1,n+1);scanf("%d",&m);for(int i=1;i<=m;i++){scanf("%d",&x);if(x==1){scanf("%d %d",&a,&add1);add(1,a,add1);}else{scanf("%d %d",&a,&b);printf("%d\n",query(1,a,b+1));}}return 0;
}
/*
树状数组 单点修改 区间求和.
比线段树不知道要快到那里去. 
*/
#include<cstdio>
#include<iostream>
#define MAXN 100001
int s[MAXN],n,x,y,m,z;
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;
}
int lowbit(int t){return t&-t;
}
void add(int t,int x){while(t<=n){s[t]+=x;t+=lowbit(t);}
}
int query(int x){int tot=0;while(x){tot+=s[x];x-=lowbit(x);}return tot; 
}
int main(){n=read();for(int i=1;i<=n;i++){x=read();add(i,x);}m=read();for(int i=1;i<=m;i++){z=read();x=read();y=read();if(z==1)  add(x,y);else printf("%d\n",query(y)-query(x-1));}return 0;
}
/*
分块.
单点修改 区间查询.
*/
#include<cstdio>
#include<cmath>
#include<iostream>
#define MAXN 100001
using namespace std;
int n,m,q,ans,s[MAXN],belong[MAXN],sum[MAXN];
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;
}
int slovequery(int x,int y)
{ans=0;for(int i=x;i<=min(y,belong[x]*m);i++) ans+=s[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];return ans;
}int main()
{int x,y,z;n=read();m=sqrt(n);for(int i=1;i<=n;i++) belong[i]=(i-1)/m+1;for(int i=1;i<=n;i++) s[i]=read(),sum[belong[i]]+=s[i];q=read();while(q--){z=read(),x=read(),y=read();if(z&1) s[x]+=y,sum[belong[x]]+=y;else printf("%d\n",slovequery(x,y));}return 0;
}
/*
CDQ分治。
对x坐标升序,
CDQ分治 
维护一个前缀贡献。 
*/
#include<algorithm>
#include<cstdio>
#define MAXN 100001
using namespace std;
int s[MAXN],n,m,tot,t,sum,ans[MAXN];
struct data{int x,k,t,o,z,belong;}q[MAXN*2],tmp[MAXN*2];
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;
}
bool cmp(const data &x,const data &y)
{if(x.x!=y.x) return x.x<y.x;else return x.k<y.k;
}
void slove(int l,int r)
{if(l==r) return ;int mid=(l+r)>>1,ll=l,rr=mid+1;for(int i=l;i<=r;i++){if(q[i].k==1&&q[i].t<=mid) sum+=q[i].z;else if(q[i].k==2&&q[i].t>mid) ans[q[i].belong]+=sum*q[i].z;}for(int i=l;i<=r;i++){if(q[i].t<=mid) tmp[ll++]=q[i];else tmp[rr++]=q[i];}sum=0;for(int i=l;i<=r;i++) q[i]=tmp[i];slove(l,mid),slove(mid+1,r);return ;
}
int main()
{int x,y,z;n=read();for(int i=1;i<=n;i++) x=read(),q[++tot].x=i,q[tot].k=1,q[tot].z=x,q[tot].t=tot;m=read();for(int i=1;i<=m;i++){z=read(),x=read(),y=read();if(z&1)  q[++tot].x=x,q[tot].k=1,q[tot].z=y,q[tot].t=tot;else{q[++tot].x=x-1,q[tot].k=2,q[tot].z=-1,q[tot].t=tot,q[tot].belong=++t;q[++tot].x=y,q[tot].k=2,q[tot].z=1,q[tot].t=tot,q[tot].belong=t;}}sort(q+1,q+tot+1,cmp);slove(1,tot);for(int i=1;i<=t;i++) printf("%d\n",ans[i]);return 0;
}

“`

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



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

相关文章

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

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

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

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 ;