1.动态求连续区间和
题目
给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。
输入格式
第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。
第二行包含 n 个整数,表示完整数列。
接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。
数列从 1 开始计数。
输出格式
输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。
数据范围
1≤n≤100000,
1≤m≤100000,
1≤a≤b≤n
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35
分析
两种解法
1.树状数组
2.线段树
代码
/*
1.输入 n:数的个数 m :操作次数 1≤n≤100000,
注意:下标从 1 开始
2.判断操作
3.进行操作
0 : 区间求和
1 :单点加数
4.输出结果
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int w[N];
struct Node //定义线段树的节点
{
int l,r;
int sum;
}tr[N * 4]; //线段树的数组长度大约是原数组的4倍
//查找的过程中l和r始终不改变
//创建的过程中l和r始终在改变
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; //等于左孩子 + 右孩子
}
void build(int u,int l,int r) //build 在1~n 的区间上初始化线段树————递归
{
if(l == r) tr[u] = {
l,r,w[r]}; //tr[u] = {l,r,w[r]}; 这种赋值方式存在点问题 C++11能不能用
else
{
tr[u] = {
l,r}; //初始化 sum 为 0
//一层一层向下递归
int mid = l + r >> 1;
build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
pushup(u);
}
}
int query(int u,int l,int r) //求区间的和
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
//一层一层向下递归
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if(l <= mid) sum += query(u << 1,l,r);
if(r > mid) sum += query(u << 1 | 1,l,r);
return sum;
}
void modify(int u,int x,int v)
{
if(tr[u].l == tr[u].r) tr[u].sum += v; //只有一个节点的时候
else
{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1,x,v);
else modify(u << 1 | 1,x,v);
pushup(u);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)
scanf("%d",&w[i]);
build(1,1,n); //build : 在1 ~ n 区间上初始化线段树 第一个1 父节点
int k,a,b;
while(m--)
{
scanf("%d%d%d",&k,&a,&b);
if(k == 0) printf("%d\n",query(1,a,b)); //query 求区间和
else modify(1,a,b); //modify 在a的位置上加上b
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int n,m;
int a[N],tr[N];
int lowbit(int x)
{
return x & (-x);
}
//创建并且加上V
void add(int x,int v)
{
for(int i = x;i <= n;i += lowbit(i)) tr[i] = tr[i] + v;
}
//计算前缀和
int query(int x)
{
int res = 0;
for(int i = x;i;i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
int k,x,y;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
for(int i = 1;i <= n;i++) add(i,a[i]); //创建并且加上v
while(m--)
{
scanf("%d%d%d",&k,&x,&y);
if(k == 0) printf("%d\n",query(y) - query(x - 1));
else add(x,y);
}
return 0;
}
2.数星星
题目
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。
如果一个星星的左下方(包含正左和正下)有 k 颗星星,就说这颗星星是 k 级的。
例如,上图中星星 5 是 3 级的(1,2,4 在它左下),星星 2,4 是 1 级的。
例图中有 1 个 0 级,2 个 1 级,1 个 2 级,1 个 3 级的星星。
给定星星的位置,输出各级星星的数目。
换句话说,给定 N 个点,定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。
输入格式
第一行一个整数 N,表示星星的数目;
接下来 N 行给出每颗星星的坐标,坐标用两个整数 x,y 表示;
不会有星星重叠。星星按 y 坐标增序给出,y 坐标相同的按 x 坐标增序给出。
输出格式
N 行,每行一个整数,分别是 0 级,1 级,2 级,……,N−1 级的星星的数目。
数据范围
1≤N≤15000,
0≤x,y≤32000
输入样例:
5
1 1
5 1
7 1
3 3
5 5
输出样例:
1
2
1
1
0
分析
原始数组是 a[i] 表示横坐标 == i时的点的个数
无需体现出来
tr[i] 无意义,中间量
代码
/*
1.输入 N :星星数目
输入N行坐标(x,y) y坐标正序
2.得出每一级星星的数目
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 32010;
int n;
int tr[N