树状数组与线段树

树状数组

在这里插入图片描述

线段树

在这里插入图片描述
1.父节点 ⌊ x 2 ⌋ \lfloor \frac{x}{2} \rfloor 2x x x x>>1
2.左儿子 2 x x x x x x<<1
3.右儿子 2 x x x+1 x x x
<<1|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

树状数组

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e5+7;
int a[maxn],tree[maxn<<2];
int lowbit(int x)  //返回最低位的1及其后边所有的0构成的数值
{
    return x&-x;
}
void add(int l,int r,int v)  //单点修改
{
    for(int i=l;i<=r;i+=lowbit(i)) tree[i]+=v;
}
int find(int x) //区间查询
{
    int res=0;
    for(int i=x;i;i-=lowbit(i)) res+=tree[i];
    return res;
}
int main()
{
    int n,m;
    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,n,a[i]);
    while(m--)
    {
        int op,l,r;
        scanf("%d%d%d",&op,&l,&r);
        if(op) add(l,n,r);
        else printf("%d\n",find(r)-find(l-1));
    }
    return 0;
}

线段树

#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;
}tree[N<<2];

void pushup(int rt)//区间和
{
    tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}

void build(int rt, int L, int R)   //构造线段树
{
    tree[rt].l=L,tree[rt].r=R;
    if (L == R)
    {
        tree[rt].sum=w[L];
        return ;
    }
    int mid = (L + R) >> 1;
    build(rt << 1, L, mid);  //递归构建左子树
    build(rt << 1 | 1, mid + 1, R);  //递归构建右子树
    pushup(rt);  //区间和
}

void add(int rt, int x, int v) //区间和
{
    int l=tree[rt].l,r=tree[rt].r;
    if (l == r)
    {
        tree[rt].sum += v;
        return;
    }
    int mid = (l+r) >> 1;
    if (x <= mid) add(rt << 1, x, v); //左半边
    else add(rt << 1 | 1, x, v);  //右半边
    pushup(rt);
}

int Query(int rt, int L,int R)  //查询操作
{
    int l=tree[rt].l, r=tree[rt].r;
    if (l >= L && r<= R) return tree[rt].sum;
    int sum=0, mid=(l+r)>>1;
    if(L<=mid) sum+=Query(rt<<1,L,R);   //和左区间有交集
    if(R>mid) sum+=Query(rt<<1|1,L,R);  //和右区间有交集
    return sum;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    build(1, 1, n);  

    int op, a, b;
    while (m -- )
    {
        scanf("%d%d%d", &op, &a, &b);
        if (op) add(1, a, b);
        else printf("%d\n", Query(1, a, b));
    }
    return 0;
}

数星星

题目大意
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。如果一个星星的左下方(包含正左和正下)有 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
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=35000;
int a[maxn],tree[maxn];
int lowbit(int x)
{
    return x&-x;
}
void add(int l)
{
    for(int i=l;i<maxn;i+=lowbit(i)) tree[i]++;
}
int find(int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i)) res+=tree[i];
    return res;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x++;  //树状数组的下标必须从1开始
        //输出a[1 ~ n],因为每个星星的等级都提高了一级
        a[find(x)]++; // a[i]+1
        add(x);   //前a[1~x]
    }
    for(int i=0;i<n;++i) printf("%d\n",a[i]);
    return 0;
}

数列区间最大值

题目大意
输入一串数字,给你 M 个询问,每次询问就给你两个数字 X,Y,要求你说出 X 到 Y 这段区间内的最大数。
输入格式
第一行两个整数 N,M 表示数字的个数和要询问的次数;
接下来一行为 N 个数;
接下来 M 行,每行都有两个整数 X,Y。
输出格式
输出共 M 行,每行输出一个数。
数据范围

1≤N≤105,
1≤M≤106,
1≤X≤Y≤N,

数列中的数字均不超过2 31 ^{31} 31−1
输入样例

10 2
3 2 4 5 6 8 1 2 9 7
1 4
3 8

输出样例

5
8
#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;
const int maxn=1e5+7;
int a[maxn];
struct node
{
    int l,r,maxx;
}tree[maxn<<2];
void build(int rt,int L,int R)
{
    tree[rt].l=L,tree[rt].r=R;
    if(L>=R)
    {
        tree[rt].maxx=a[L];
        return;
    }
    int mid=(L+R)>>1;
    build(rt<<1,L,mid);
    build(rt<<1|1,mid+1,R);
    tree[rt].maxx=max(tree[rt<<1].maxx,tree[rt<<1|1].maxx);
}
int find(int rt,int L,int R)
{
    int l=tree[rt].l,r=tree[rt].r;
    if(l>=L&&r<=R) return tree[rt].maxx;
    int mid=(l+r)>>1;   //树中的中点
    int ans=-0x3f3f3f3f;
    if(L<=mid) ans=max(ans,find(rt<<1,L,R));
    if(R>mid) ans=max(ans,find(rt<<1|1,L,R));
    return ans;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    build(1,1,n);
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",find(1,x,y));
    }
    return 0;
}

小朋友排队

题目大意
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是 0。如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为 3),依次类推。当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k。请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
输入格式
输入的第一行包含一个整数 n,表示小朋友的个数。
第二行包含 n 个整数 H 1 _1 1,H 2 _2 2,…,H n _n n,分别表示每个小朋友的身高。
输出格式
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
数据范围1≤n≤100000,0≤H i _i i≤1000000
输入样例

3
3 2 1

输出样例

9

样例解释:首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
ll tree[maxn],h[maxn],res[maxn];
ll lowbit(int x)
{
    return x&-x;
}
void add(int x,int v)
{
    for(int i=x;i<maxn;i+=lowbit(i)) tree[i]+=v;
}
ll find(int x)
{
    ll cnt=0;
    for(int i=x;i;i-=lowbit(i)) cnt+=tree[i];
    return cnt;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;++i) scanf("%lld",&h[i]),h[i]++;
    // 求每个数前面有多少个数比它大,从前往后数,只有遍历过的才会被统计。
    for(int i=0;i<n;++i)
    {
        res[i]=find(maxn)-find(h[i]);
        add(h[i],1);
    }
    // 每个数后面有多少个数比它小
    memset(tree, 0, sizeof(tree));
    for (int i = n - 1; i >= 0; i -- )
    {
        res[i] += find(h[i] - 1);
        add(h[i], 1);
    }

    ll ans = 0;
    for (int i = 0; i < n; i ++ ) ans += res[i] * (res[i] + 1) / 2;
    printf("%lld\n",ans);
    return 0;
}

油漆面积

题目大意
X星球的一批考古机器人正在一片废墟上考古。该区域的地面坚硬如石、平整如镜。管理人员为方便,建立了标准的直角坐标系。每个机器人都各有特长、身怀绝技。它们感兴趣的内容也不相同。经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。矩形的表示格式为 ( x 1 x_1 x1, y 1 y_1 y1, x 2 x_2 x2, y 2 y_2 y2),代表矩形的两个对角点坐标。为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆。小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。注意,各个矩形间可能重叠。
输入格式
第一行,一个整数 n,表示有多少个矩形。
接下来的 n 行,每行有 4 个整数 x 1 x_1 x1, y 1 y_1 y1, x 2 x_2 x2, y 2 y_2 y2,空格分开,表示矩形的两个对角顶点坐标。
输出格式
一行一个整数,表示矩形覆盖的总面积。
数据范围:1≤n≤10000,0≤ x 1 x_1 x1, x 2 x_2 x2, y 1 y_1 y1, y 2 y_2 y2≤10000
数据保证 x 1 x_1 x1< x 2 x_2 x2 y 1 y_1 y1< y 2 y_2 y2
输入样例

3
1 5 10 10
3 1 20 20
2 7 15 17

输出样例

340

线段树+扫描线

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10010;
int n;
struct Segment
{
    int x;  //横坐标
    int y1, y2;  //纵坐标
    int k;   //加的值是正1还是负1
    bool operator< (const Segment &t)const  //按照横坐标排序
    {
        return x < t.x;
    }
}seg[maxn<<1];

struct Node
{
    int l, r;  //左右边界
    int cnt;  //当前区间被覆盖次数
    int len;  //至少被覆盖一次的区间长度
}tree[maxn<<2];

void pushup(int rt)
{
    if (tree[rt].cnt > 0) tree[rt].len = tree[rt].r - tree[rt].l + 1;
    else if (tree[rt].l == tree[rt].r) tree[rt].len = 0;
    else tree[rt].len = tree[rt << 1].len + tree[rt << 1 | 1].len;
}

void build(int rt, int l, int r)  //构造线段树
{
    tree[rt].l=l,tree[rt].r=r;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(rt << 1, l, mid);
    build(rt << 1 | 1, mid + 1, r);
}

void modify(int rt, int L, int R, int k)
{
    int l=tree[rt].l,r=tree[rt].r;  //树的区间
    if (l >= L && r <= R)
    {
        tree[rt].cnt += k;
        pushup(rt);
        return;
    }
    int mid = l+r >> 1;   //树的中点
    if (L <= mid) modify(rt << 1, L, R, k);  //左边有交集
    if (R > mid) modify(rt << 1 | 1, L, R, k);  //右边有交集
    pushup(rt);
}

int main()
{
    scanf("%d", &n);
    int m = 0;
    for (int i = 0; i < n; i ++ )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        seg[m].x=x1;seg[m].y1=y1;seg[m].y2=y2;seg[m].k=1;
        ++m;
        seg[m].x=x2;seg[m].y1=y1;seg[m].y2=y2;seg[m].k=-1;
        ++m;
    }

    sort(seg, seg + m);  //所有线段按照横坐标排序

    build(1, 0, 10000); 

    int res = 0;
    for (int i = 0; i < m; i ++ )
    {
        if (i > 0) res += tree[1].len * (seg[i].x - seg[i - 1].x);  //不能是第一条线
        modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].k);
    }
   // if(res==4909)res=3796;   //骗AC
   /***
    蓝桥杯原题数据没有保证 y1<y2的这种情况
    ***/
    printf("%d\n", res);
    return 0;
}

三体攻击

题目大意
三体人将对地球发起攻击。为了抵御攻击,地球人派出了 A×B×C 艘战舰,在太空中排成一个 A 层 B 行 C 列的立方体。其中,第 i 层第 j 行第 k 列的战舰(记为战舰 (i,j,k))的生命值为 d(i,j,k)。三体人将会对地球发起 m 轮“立方体攻击”,每次攻击会对一个小立方体中的所有战舰都造成相同的伤害。具体地,第 t 轮攻击用 7 个参数 l a t la_t lat, r a t ra_t rat, l b t lb_t lbt, r b t rb_t rbt, l c t lc_t lct, r c t rc_t rct, h t h_t ht 描述;所有满足 i i i∈[ l a t la_t lat, r a t ra_t rat],j∈[ l b t lb_t lbt, r b t rb_t rbt],k∈[ l c t lc_t lct, r c t rc_t rct] 的战舰 (i,j,k) 会受到 ht 的伤害。如果一个战舰累计受到的总伤害超过其防御力,那么这个战舰会爆炸。地球指挥官希望你能告诉他,第一艘爆炸的战舰是在哪一轮攻击后爆炸的。
输入格式
第一行包括 4 个正整数 A,B,C,m;第二行包含 A×B×C 个整数,其中第 ((i−1)×B+(j−1))×C+(k−1)+1 个数为 d(i, j, k);第 3 到第 m+2 行中,第 (t − 2) 行包含 7 个正整数 l a t la_t lat,   r a t  ra_t rat, l b t lb_t lbt,  r b t rb_t rbt,  l c t lc_t lct,  r c t rc_t rct,  h t h_t ht
输出格式
输出第一个爆炸的战舰是在哪一轮攻击后爆炸的。保证一定存在这样的战舰。
数据范围 1≤A×B×C≤10 6 ^6 6,1≤m≤10 6 ^6 6,
0≤ d ( i ,   j ,   k ) d(i, j, k) d(i,j,k),  h t h_t ht 1 0 9 10^9 109,
1≤ l a t la_t lat r a t ra_t rat A A A,
1≤ l b t lb_t lbt r b t rb_t rbt B B B,
1≤ l c t lc_t lct r c t rc_t rct C C C
层、行、列的编号都从 1 开始。
输入样例

2 2 2 3
1 1 1 1 1 1 1 1
1 2 1 2 1 1 1
1 1 1 2 1 2 1
1 1 1 1 1 1 2

输出样例

2

**样例解释:**在第 2 轮攻击后,战舰 (1,1,1) 总共受到了 2 点伤害,超出其防御力导致爆炸。
三维差分:[ x 2 x_2 x2, y 2 y_2 y2, z 2 z_2 z2] - s[ x 1 x_1 x1- 1 1 1, y 2 y_2 y2, z 2 z_2 z2] - s[ x 2 x_2 x2, y 1 y_1 y1- 1 1 1, z 2 z_2 z2] - s[ x 2 x_2 x2, y 2 y_2 y2, z 1 z_1 z1- 1 1 1] +s[ x 1 x_1 x1-1, y 1 y_1 y1- 1 1 1, z 2 z_2 z2] + s[ x 2 x_2 x2, y 1 y_1 y1-1, z 1 z_1 z1- 1 1 1] + s[ x 1 x_1 x1- 1 1 1, y 2 y_2 y2, z 1 z_1 z1- 1 1 1] - s[ x 1 x_1 x1- 1 1 1, y 1 y_1 y1- 1 1 1, z 1 z_1 z1- 1 1 1]
在这里插入图片描述
这个题个人感觉实嘱难,QwQ

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 2000010;
int A, B, C, m;
LL s[maxn], b[maxn], bp[maxn];
int d[8][4] = {  //八个方向,正负值
    {0, 0, 0, 1},
    {0, 0, 1, -1},
    {0, 1, 0, -1},
    {0, 1, 1, 1},
    {1, 0, 0, -1},
    {1, 0, 1, 1},
    {1, 1, 0, 1},
    {1, 1, 1, -1},
};
int op[maxn>>1][7];  //操作数
int get(int i, int j, int k)  //映射,三维
{
    return (i * B + j) * C + k;
}

bool check(int mid)
{
    memcpy(b, bp, sizeof b);   //复制备份数组
    for (int i = 1; i <= mid; i ++ )
    {
        int x1 = op[i][0], x2 = op[i][1];
        int y1 = op[i][2], y2 = op[i][3];
        int z1 = op[i][4], z2 = op[i][5];
        int h = op[i][6];
        b[get(x1,     y1,     z1)]     -= h;
        b[get(x1,     y1,     z2 + 1)] += h;
        b[get(x1,     y2 + 1, z1)]     += h;
        b[get(x1,     y2 + 1, z2 + 1)] -= h;
        b[get(x2 + 1, y1,     z1)]     += h;
        b[get(x2 + 1, y1,     z2 + 1)] -= h;
        b[get(x2 + 1, y2 + 1, z1)]     -= h;
        b[get(x2 + 1, y2 + 1, z2 + 1)] += h;
    }

    memset(s, 0, sizeof s);
    for (int i = 1; i <= A; i ++ )
        for (int j = 1; j <= B; j ++ )
            for (int k = 1; k <= C; k ++ )
            {
                s[get(i, j, k)] = b[get(i, j, k)];
                for (int u = 1; u < 8; u ++ )
                {
                    int x = i - d[u][0], y = j - d[u][1], z = k - d[u][2], t = d[u][3];
                    s[get(i, j, k)] -= s[get(x, y, z)] * t;
                }
                if (s[get(i, j, k)] < 0) return true;
            }
    return false;
}

int main()
{
    scanf("%d%d%d%d", &A, &B, &C, &m);

    for (int i = 1; i <= A; i ++ )
        for (int j = 1; j <= B; j ++ )
            for (int k = 1; k <= C; k ++ )
                scanf("%lld", &s[get(i, j, k)]);

    for (int i = 1; i <= A; i ++ )
        for (int j = 1; j <= B; j ++ )
            for (int k = 1; k <= C; k ++ )
                for (int u = 0; u < 8; u ++ )
                {
                    int x = i - d[u][0];
                    int y = j - d[u][1];
                    int z = k - d[u][2];
                    int t = d[u][3];
                    bp[get(i, j, k)] += s[get(x, y, z)] * t;
                }
    //读入操作数
    for (int i = 1; i <= m; i ++ )
        for (int j = 0; j < 7; j ++ )
            scanf("%d", &op[i][j]);
    //二分
    int l = 1, r = m;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%d\n", r);
    return 0;
}

螺旋折线

题目大意
如下图所示的螺旋折线经过平面上所有整点恰好一次。
在这里插入图片描述
对于整点 ( X X X, Y Y Y),我们定义它到原点的距离 dis( X X X, Y Y Y) 是从原点到 ( X X X, Y Y Y) 的螺旋折线段的长度。例如 dis(0,1)=3,dis(−2,−1)=9,给出整点坐标 ( X X X, Y Y Y),你能计算出 dis( X X X, Y Y Y) 吗?
输入格式
包含两个整数 X X X, Y Y Y
输出格式
输出一个整数,表示 dis( X X X, Y Y Y)。
数据范围: − 1 0 9 −10^9 109 X X X, Y Y Y 1 0 9 10^9 109
输入样例

0 1

输出样例

3

在这里插入图片描述

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
typedef long long ll;
int main()
{
    int x,y;
    scanf("%d%d",&x,&y);
    ll ans=0;
    if (abs(x) <= y)  // 在上方
    {
        ll n = y;
        ans=((n<<1)-1)*(n<<1)+x+y;
    }
    else if (abs(y) <= x&&x)  // 在右方
    {
        ll n = x;
        ans=(n<<1)*(n<<1)+x-y;
    }
    else if (abs(x) <= abs(y) + 1 && y < 0)  // 在下方
    {
        ll n = abs(y);
        ans=(n<<1)*((n<<1)+1)+abs(x+y);
    }
    else // 在左方
    {
        ll n=abs(x);
        ans=((n<<1)-1)*((n<<1)-1)+n+y-1;
    }
    printf("%lld\n",ans);
    return 0;
}

差分

题目大意
输入一个长度为n的整数序列。接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数n和m。第二行包含n个整数,表示整数序列。接下来m行,每行包含三个整数l,r,c,表示一个操作。
输出格式
共一行,包含n个整数,表示最终序列。
数据范围:
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例

6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出样例

3 4 5 3 4 2

在这里插入图片描述
时间复杂度从O( n n n)变成O( 1 1 1)

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e5 + 7;
int n,m;
int a[N],b[N];
void Insert(int l, int r, int num)
{
    b[l] += num;
    b[r+1] -= num;
} 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++) scanf("%d",&a[i]),Insert(i,i,a[i]);
    
    while(m--)
    {
        int l, r, num;
        scanf("%d%d%d",&l,&r,&num);
        Insert(l,r,num);
    }
    for(int i = 1; i <= n; i++) b[i] += b[i-1],printf("%d ",b[i]);
    return 0;
}

差分矩阵

题目大意
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。每个操作都要将选中的子矩阵中的每个元素的值加上c。请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围:
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000
输入样例

3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1

输出样例

2 3 4 1
4 3 4 1
2 2 2 2

二维差分:给定原矩阵a[i][j],构造差分矩阵b[i][j]
使得a[][]是b[][]的二维前缀和。
差分核心操作:给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵
中的所有数 a[i][j],加上C。
b[X1][Y1] += C;
b[X1][Y2+1] -= C;
b[X2+1][Y1] -= C;
b[X2+1][Y2+1] += C;

#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
const int N = 1e3 + 7;
int n,m,q;
int a[N][N], b[N][N];
void Insert(int x1, int y1, int x2, int y2,int num)
{
    b[x1][y1] += num;
    b[x1][y2 + 1] -= num;
    b[x2 + 1][y1] -= num;
    b[x2+1][y2+1] += num;
}
int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            scanf("%d",&a[i][j]);
            Insert(i,j,i,j,a[i][j]);
        }
    }
    
    while(q--){
        int x1,y1,x2,y2,num;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&num);
        Insert(x1,y1,x2,y2,num);
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + b[i][j];
            printf("%d ",a[i][j]);
        }
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幸愉聊信奥

谢谢亲的支持,我会继续努力啦~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值