数组数组 线段树 st表
树状数组 o logn 在某个位置加上某个数(如果是修改,那么就加两个数的差 ),求前缀和(求前缀和本身不支持修改),从而求区间和
奇数位置 存放的原来数组a上的位置的数
偶数位置 存放前一个奇数位置+前一个偶数位置的数
x的二进制表示有k个0
c[x]=【x-2^k ,x】
c[x]=(x-lowbit(x),x)
三个操作+初始化
lowbit
add
query查询
初始化 让tri[i]每个位置加上原来数组a[i]
int lowbit(int x){
return x&-x;
}
void add(int x,int y){//在第x个数加上y
//后面每个影响本身和本身之后后面每个和2的倍数有关的数
for(int i=x;i<=n;i+=lowbit(i))//两个lowbit都是对i操作 tri[i]+=v;
}
int query(int x){修改1到x的和
int res=0;
for(int i=x;i;i-=lowbit(i)) //两个lowbit都是对i操作
res+=tri[i];
return res;
}
初始化
数星星 给出二维平面上的 坐标求每个坐标上左下方有几个坐标表示第几级,求每个等级邮寄给坐标#
因为一层层一层给出 可以抽象成带修改+1 的前缀和,求得到的前缀和对应结果加1
因为数据输入的是按y不降输入的,而 x 可以是任意的,如果我们不实时计算,而是等到全部处理完再计算的话,会导致 “x 虽然比你大但是 y 比你小的情况”,
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 32010;
int n;
int tr[N],level[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x) // 位置x加1
{
for (int i = x; i <= N; i += lowbit(i)) tr[i] += 1;
}
int query(int x) // 返回前x个数的和
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
cin>>n;
for (int i = 0; i < n; i ++ ){
int x,y;
scanf("%d%d",&x,&y);
x++;//坐标操作对应x=1防止越界
level[query(x)]++;//求x左边的数
add(x);//坐标不重复,这个x坐标上的星星数+1
}
for (int i = 0; i < n; i ++ ) cout<<level[i]<<endl;
return 0;
}
线段数组
单点修改
区间查询 使用最小包含
四个操作
pushup 向上更新
build 初始话
modify 修改
query 查询
父亲结点 x>>1
左儿子 x<<1 右儿子 x<<1|1 等价2*x+1
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m;
int w[N];
struct Node
{
int l, r;
int sum;
}tr[N * 4];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;//找儿子结点的位运算向左
}
void build(int u, int l, int r)//l和r需要改变l到mid mid+1到r
{
if (l == r) tr[u] = {l, r, w[r]};
else
{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);//找mid的位运算向右
pushup(u);//上面做完都只是叶子结点有值但是上面的是没有值的 因为pushup所以才从叶子找到了值
}
}
int query(int u, int l, int r)//查询过程中l和r是不变的 变的是跟结点他所代表的值mid
{
if (l <= tr[u].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);//查询有交集的区间 如果查询l=2 r=5 但已经查询到(1,2)代表的区间 mid=1.5 发现l向左边查 l已经被
if (r > mid) sum += query(u << 1 | 1, l, r);//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;//注意是mid是为了找数据 所以应该在u代表的数据中找
if (x <= mid) modify(u << 1, x, v);//分开 发现x在mid的左边就往左边递归 在右边就往右边递归
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);
int k, a, b;
while (m -- )
{
scanf("%d%d%d", &k, &a, &b);
if (k == 0) printf("%d\n", query(1, a, b));
else modify(1, a, b);
}
return 0;
}
区间最值 https://www.acwing.com/problem/content/1275/#
st表
注意分清楚 长度 和 端点 的关系 f[i][j]的区间是 [i, i+(2^j) -1 ]
const int N = 2e5+10,M=18;
int q[N];
int f[N][M];int n,m;
int main()
{
cin>>n;
for (int i = 1; i <= n; i ++ ) cin >> q[i];
cin >> m;
for (int j = 0; j < M; j ++ ){
for (int i = 1; i+(1<<j)-1 <= n; i ++ ){
if(!j) f[i][j]=q[i];
else f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
while (m -- ){
int a,b;cin>>a>>b;
int len=b-a+1;
int k=log(len)/log(2);
cout <<max( f[a ][k ],f[b+1-(1<<k) ][ k] )<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库