ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

蓝桥杯 第五讲 树状数组和线段树 2D/3D差分

2022-02-02 18:01:11  阅读:189  来源: 互联网

标签:2D 结点 int lowbit sum tr 蓝桥 include 3D


一、树状数组 下标从1开始

操作 O(logn)

  1. 单点修改: 给某个位置上的数加上一个数
  2. 区间查询:求某一个前缀和

离线做法:不支持修改
在线做法:支持修改

原理

image

层数的确定:x的二进制表示中末尾有几个0
c[x] = (x-lowbit(x),x]的和
lowbit(x):返回x的二进制表达式中最低位的1所对应的值
lowbit(6) = 2,因为(110)2中最低位(就是从右往左数的第二位)对应的数是21=2
1264. 动态求连续区间和

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100005;

int a[N],n,m;
int tr[N];//tr[x]表示(x-lowbit(x),x]的和
int k,x,y;
int lowbit(int x)
{
    return x & -x;
}
void add(int x,int v) //第x的位置上加上一个数
{
    for(int i = x;i <= n;i+=lowbit(i))
    {
        tr[i] += v;
    }
}
int query(int x) //查询 1~x的前缀和
{
    int res = 0;
    for(int i = x;i>=1;i-=lowbit(i))
    {
        res += tr[i];
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i = 1; i <= n; i ++ ) //切记下标要从1开始
    {
        scanf("%d", &a[i]);
        add(i,a[i]);
    }
    while (m -- )
    {
        scanf("%d%d%d",&k,&x,&y);
        if(k==0)
        {
            cout<<query(y) - query(x-1)<<endl; //前缀和思想
        }
        else add(x,y);
    }
    return 0;
}

1265. 数星星
由于坐标按照y升序给出,故当前星星的坐标一定大于等于之前任何星星,故需要统计小于等于当前星星的x的个数,即为左下方星星个数

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 32005;
int n,x,y;
int tr[N];//x坐标小于等于i的星星的个数
int level[N];//不同等级星星的个数
int lowbit(int x)
{
    return x & -x;
}
void add(int t,int x)
{
    for(int i=t;i<=N;i+=lowbit(i)) //注意:此处范围是[t,N],x的取值会比n大,WA了好几发
    {
        tr[i] += x;
    }
}
int query(int t)
{
    int sum = 0;
    for(int i=t;i>=1;i-=lowbit(i))
    {
        sum += tr[i];
    }
    return sum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) //由于坐标按照y升序给出,故当前星星的坐标一定大于等于之前任何星星,故需要统计小于等于当前
    //星星的x的个数,即为左下方星星个数
    {
        scanf("%d%d", &x, &y);
        x++;//将x映射到1~N+1,便于使用树状数组
        level[query(x)]++;
        add(x,1);
    }
    for (int i = 0; i < n; i ++ ) //注意:此处范围为0~n-1,不可能有等级为n的星星
    {
        cout<<level[i]<<endl;
    }
    return 0;
}

二、线段树

以求区间和为例
image

核心操作 O(logn)

1. 单点修改

递归地进行修改,只要此区间包含要修改的点,就修改区间的sum

2. 区间查询

递归地查询,只要该区间的[l,r]被目标区间完全包含,则累加并回溯,否则继续向下递归查询。
查询次数小于等于4logn
image

主要函数

pushup用子节点信息更新当前结点信息
build在一段区间上初始化线段树
modify修改
query查询
pushdown懒标记
1264. 动态求连续区间和

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e5+10;

int n,m,k,a,b;
int w[N];

struct Node
{
    int l;
    int r;
    int sum;
}tr[N*4]; //线段树最大结点个数为4N

void pushup(int u) //用左右儿子结点信息更新根结点信息
{
    tr[u].sum = tr[2*u].sum + tr[2*u+1].sum;
}

void build(int u,int l,int r) //形参解释:u表示根结点,l,r表示原始数据区间的左右端点
{
    if(l==r) tr[u] = {l,r,w[r]};//区间元素个数为1,是叶结点,直接赋值返回
    else
    {
        tr[u] = {l,r};//注意:此处一定要赋初值
        int mid = l+r>>1;
        build(u*2,l,mid),build(u*2+1,mid+1,r);//递归建立左右儿子区间
        pushup(u);//一定不要忘记更新根结点sum值,将信息向上传递
    }
}
int query(int u,int l,int r)//形参解释:u表示根结点,l,r表示目标区间的左右端点
{
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum; //如果当前区间被目标区间完全包含,则返回当前区间sum
    
    int mid = tr[u].l+tr[u].r>>1;
    int sum = 0;
    if(l <= mid) sum += query(u*2,l,r); //如果目标区间的左边界≤mid,说明和左半边有交集
    if(r > mid) sum += query(u*2+1,l,r);//如果目标区间的右边界>mid,说明和右半边有交集
    return sum;
}
void modify(int u,int x,int v) //形参解释:u表示根结点,x表示元素下标,v表示要添加的值
{
    if(tr[u].l == tr[u].r) //若走到叶结点,说明已经找到x对应树中的位置,直接修改即可
    {
        tr[u].sum += v;
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(x <= mid) modify(u*2,x,v);//如果和左半边有交集,就去左半边修改
        else modify(u*2+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);
    while (m -- )
    {
        scanf("%d%d%d",&k,&a,&b);
        if(k==0)
        {
            cout<<query(1,a,b)<<endl;
        }
        else modify(1,a,b);
    }
    return 0;
}

1270. 数列区间最大值

#include <iostream>
#include <cstring>
#include <algorithm>
#include <climits>
using namespace std;

const int N = 1e5 + 10;
int w[N];
int n,m,x,y;
struct Node
{
    int l;
    int r;
    int Max;
}tr[N*4];
inline void build(int u,int l,int 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);
        tr[u].Max = max(tr[u<<1].Max,tr[u<<1|1].Max);
    }
}

inline int query(int u,int l,int r)
{
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].Max;
    
    int Max = INT_MIN;
    int mid = tr[u].l + tr[u].r>>1;
    if(l <= mid) Max = max(Max,query(u<<1,l,r));
    if(r > mid) Max = max(Max,query(u<<1|1,l,r));
    return Max;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &w[i]);
    }
    build(1,1,n);
    while (m -- )
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",query(1,x,y));
    }
    return 0;
}

三、2D/3D差分

二维差分

image
798. 差分矩阵

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1005;

int b[N][N];
int n,m,q;

void insert(int x1,int y1,int x2,int y2,int c) //x2,y2一定要加1
{
    b[x1][y1] += c;
    b[x2+1][y2+1] += c;
    b[x1][y2+1] -= c;
    b[x2+1][y1] -= c;
}
int main()
{
    scanf("%d%d%d", &n, &m,&q);
    int c;
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
        {
            scanf("%d", &c);
            insert(i,j,i,j,c);
        }
    }
    while (q -- )
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d%d", &x1, &y1,&x2,&y2,&c);
        insert(x1,y1,x2,y2,c);
    }
    for (int i = 1; i <= n; i ++ )//求前缀和,即将差分矩阵转化为原矩阵
    {
        for (int j = 1; j <= m; j ++ )
        {
            b[i][j] = b[i-1][j] + b[i][j-1] - b[i-1][j-1] + b[i][j];
        }
    }
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
        {
            printf("%d ",b[i][j]);
        }
        puts("");
    }
    return 0;
}

三维差分

image

公式中对于S来说,奇数个减一的符号为正,偶数个减一的符号为负

四、习题

1215. 小朋友排队
本题需要求每个数的逆序对的个数,然后逐一使用等差数列求和公式算出答案即可,求逆序对的数量时需要用到线段树

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N = 1e6 + 10;

int h[N],tr[N],n;//线段树记录每个数的个数
int sum[N];
int lowbit(int x)
{
    return x & -x;
}
int query(int p)
{
    int sum = 0;
    for(int i=p;i>=1;i-=lowbit(i))
    {
        sum += tr[i];
    }
    return sum;
}
void add(int p,int x)
{
    for(int i=p;i<=N;i+=lowbit(i))
    {
        tr[i]+=x;
    }
}
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &h[i]);
        ++h[i];//计算前缀和,需要映射到1~N+1
    }
    for(int i=0;i < n;i++) //求每个数前面有多少个数比它大
    {
        sum[i] += query(N-1) - query(h[i]);
        add(h[i],1);
    }
    memset(tr,0,sizeof tr);//记得将线段树清零
    for (int i = n-1; i >=0 ; i -- )//求每个数后面有多少个数比它小
    {
        sum[i] += query(h[i]-1);
        add(h[i],1);
    }
    LL ans = 0;//答案可能爆int
    for(int i = 0;i<n;i++)
    {
        //cout<<sum[i]<<" ";
        ans += (LL)(1+sum[i])*sum[i]/2;
    }
    cout << ans<<endl;
    return 0;
}

1228. 油漆面积

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 10005;

int x1,y1,x2,y2,n;
LL ans;
bool g[N][N];
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d%d%d%d", &x1, &y1,&x2,&y2);
        for(int j=x1;j<x2;j++)
        {
            for(int k=y1;k<y2;k++) 
            {
                if(!g[j][k])
                {
                    g[j][k] = 1;
                    ++ans;
                }
            }
        }
    }
    cout << ans << endl;
    return 0;
}

1232. 三体攻击

标签:2D,结点,int,lowbit,sum,tr,蓝桥,include,3D
来源: https://www.cnblogs.com/zjq182/p/15856436.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有