树状数组的一些想法和笔记


在这里插入图片描述

问题 A: 树状数组操作

题目描述

给你n个数,创建一个树状数组,并执行相应操作,按格式要求输出操作结果。执行的操作有以下两种形式:

C i dt ,表示更新A[i],使得A[i]=A[i]+dt,其中1<=i<=n;

Q i j ,表示询问区间和,即A[i]+A[i+1]+…+A[j]的值,其中1<=i<=j<=n。

输入

第一行一个正整数n(1<=n<=10000),代表数据个数。

接下来一行是n个数据。

接下来一行一个正整数m,代表m个操作。

接下来m行,每行一个操作,格式如上所述。

输出

第一行输出创建后的树状数组,数据之间以空格分隔。

接下来m行,输出m个操作结果:对每个“C i dt ”操作,输出更新后的树状数组,数据间以空格分隔;对每个“Q i j ”操作,输出一个数值。

样例输入

10
1 3 2 6 7 -2 5 8 4 10
5
Q 1 10
C 2 4
Q 2 6
C 6 -3
Q 6 6

样例输出

1 4 2 12 7 5 5 30 4 14
44
1 8 2 16 7 5 5 34 4 14
20
1 8 2 16 7 2 5 31 4 14
-5

代码

#include<iostream>
#include<string.h>
using namespace std;

int n,m;
int a[100],c[100]; //对应原数组和树状数组

int lowbit(int x){
    
    //求sum的时候用到
    return x&(-x);
}

void updata(int i,int k){
    
        //在i位置加上k
    while(i <= n){
    
    
        c[i] += k;
        i += lowbit(i);//更新i以后的前缀和
    }
}

int getsum(int i){
    
            //求A[1 - i]的和
    int res = 0;
    while(i > 0){
    
    
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

int main()
{
    
    
    memset(a, 0, sizeof a);
    memset(c, 0, sizeof c);//全部初始化为0
    cin>>n;
    //创建树状数组
    for(int i = 1; i <= n; i++){
    
    
        cin>>a[i];
        updata(i,a[i]);   //输入初值的时候,也相当于更新了值
    }
    for(int i=1;i<=n;i++){
    
    
        if(i==1)cout << c[i];
        else cout << ' ' << c[i];
    }
    cout << endl;
    cin >> m;
    while(m--){
    
    
        char temp;
        int a,b;
        cin >> temp >> a >> b;
        if(temp=='Q'){
    
    
            cout << getsum(b)-getsum(a-1) << endl;;
        }
        if(temp=='C'){
    
    
            updata(a,b);
            for(int i=1;i<=n;i++){
    
    
                if(i==1)cout << c[i];
                else cout << ' ' << c[i];
            }
            cout << endl;
        }
    }
    return 0;
}

问题 B: 逆序对

题目描述

给你n个数,每个数a[i]都是不超过109的非负整数。求其中逆序对的个数,即所有这样的数对(i , j )满足1<=i<j<=n且a[i]>a[j]。要求用树状数组的相关操作完成题目。

输入

第一行一个正数n(1<=n<=100000),表示数据的个数。

接下来一行是n个整数。

输出

第一行一个整数m,代表逆序对的个数。

样例输入

5
4 7 2 10 9

样例输出

3

代码

#include<iostream>
#include<string.h>
using namespace std;

int n,big=-999;
int a[100],c[100]; //对应原数组和树状数组

int lowbit(int x){
    
    //求sum的时候用到
    return x&(-x);
}

void updata(int i,int k){
    
        //在i位置加上k
    while(i <= big){
    
    
        c[i] += k;
        i += lowbit(i);//更新i领导的前缀和
    }
}

int getsum(int i){
    
            //求A[1 - i]的和
    int res = 0;
    while(i > 0){
    
    
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}

int main()
{
    
    
    memset(a, 0, sizeof a);
    memset(c, 0, sizeof c);//全部初始化为0
    cin>>n;
    int sum=0;
    int t[100]={
    
    0};
    for(int i=1;i<=n;i++){
    
    //可以优化,这里偷懒了
        cin >> t[i];
        if(t[i]>big)big=t[i];
    }
    //创建树状数组
    for(int i = 1; i <= n; i++){
    
    
        int temp=t[i];
        updata(temp,1);
        sum+=i-getsum(temp);
    }
    cout << sum;
    return 0;
}

问题 C: 矩阵操作

题目描述

给定一个n✖n的矩阵A,其中每个元素不是0就是1。A[i,j]表示在第i行、第j列的数,初始时,A[i,j]=0 (1<=i,j<=n)。

我们可以按照如下方式改变矩阵:给定一个左上角在(x1,y1)、右下角在(x2,y2)的矩形,通过使用“not”操作改变这个矩形内的所有元素值(元素0变成1,元素1变成0)。为了维护矩阵的信息,你需要写个程序来接收并且执行以下两个操作:

(1)C x1 y1 x2 y2 (1<=x1<=x2<=n, 1<=y1<=y2<=n),表示更新操作,将改变左上角为(x1,y1)、右下角为(x2,y2)的矩形区域内的数据值,若元素值为0,则变成1;若元素值为1,则变成0。

(2)Q x y (1<=x,y<=n),表示询问操作,询问A[x,y]的值。

输入

第一行两个整数n和T(2<=n<=1000, 1<=T<=50000),分别代表方阵大小和操作数。

接下来T行,每行包含一个操作,以“C x1 y1 x2 y2”或者“Q x y”的形式给出,具体描述如上。

输出

对每个询问操作,输出一行一个整数,表示相应矩阵元素的值。

样例输入

2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1

样例输出

1
0
0
1

代码

#include<iostream>
#include<string.h>
using namespace std;
//差分数组


int c[1000][1000];//用来存翻转次数
int n,m;


int Lowbit(int x)
{
    
    
	return x & (-x);
}

void Updata(int x,int y)//将以x,y为右下角的矩形翻转
{
    
    
	for(int i=x; i<=n; i+=Lowbit(i))
		for(int j=y; j<=n; j+=Lowbit(j))
			c[i][j]++;//更新翻转次数
}

int Get(int x,int y)
{
    
    
	int sum = 0;
	for(int i=x; i>0; i-=Lowbit(i))
		for(int j=y; j>0; j-=Lowbit(j))
			sum += c[i][j];//求二维前缀和
	return sum;
}

int main()
{
    
    
	int x1,y1,x2,y2;

    memset(c,0,sizeof(c));//将二维数组初始化为0

    cin >> n >> m;

    while(m--){
    
    
        char temp;
        cin >> temp;
        if(temp=='C'){
    
    //翻转
            cin >> x1 >> y1 >> x2 >> y2;
            x1++; y1++; x2++; y2++;

            Updata(x2,y2);
            Updata(x1-1,y1-1);
            Updata(x1-1,y2);
            Updata(x2,y1-1);
        }else if(temp=='Q'){
    
    //查值
            cin >> x1 >> y1;
            cout << Get(x1,y1)%2 << endl;//翻转偶数次值为0,翻转奇数次值为1
        }
    }
    return 0;
}

一些参考的博客

https://blog.csdn.net/zxy_snow/article/details/6264135
https://www.cnblogs.com/ljc20020730/p/7207741.html
https://www.cnblogs.com/bluefly-hrbust/p/10416808.html
https://blog.csdn.net/qq_44786250/article/details/100056975
https://wenku.baidu.com/view/1e51750abb68a98271fefaa8.html
https://www.cnblogs.com/xenny/p/9739600.html

猜你喜欢

转载自blog.csdn.net/NP_hard/article/details/111084420