序列算法(树状数组在这儿!

区间查询&单点修改:

给定一个序列a,进行很多次操作:
访问a[l ~~ r]的区间和;
将a[i] 的值修改为 a[i] + k;


求区间x ~~ y中的区间和:

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 #define MAXN 2222
 5 
 6 int n, m;
 7 int a[MAXN], s[MAXN];
 8 
 9 int main() {
10     scanf("%d%d", &n, &m);
11     for(int i = 1; i <= n; i++) {
12 
13         scanf("%d", &a[i]);
14         s[i] = s[i - 1] + a[i];    //前缀和
15     }
16     for(int i = 1, x, y; i <= m; i++) {//m次操作
17         scanf("%d%d", &x, &y);
18         printf("%d\n", s[y] - s[x - 1]);//s[y] - s[x - 1]即为x ~~ y的区间和
19     }
20     return 0;
21 }
22 /*
23 10 100
24 1 9 5 6 8 7 4 3 6 9
25 */

 

树状数组:

**/*树状数组:动态维护前缀和*/**

 1 #include<cstdio>
 2 
 3 #define lowbit(x) (x & -x)
 4 #define MAX 11111
 5 int n,m;
 6 int t[MAX],a[MAX];//t为树状数组
 7 
 8 void add(int x, int k) {//单点修改(向树上走)
 9     for(int i = x; i <= n; i += lowbit(i)) {
10         t[i] += k;
11     }
12 }
13 
14 int query(int x) {//区间查询(在树下向前推进)//求的是前缀和
15     int sum = 0;
16     for(int i = x; i; i -= lowbit(i)) {
17         sum += t[i];
18     }
19     return sum;
20 }
21 
22 void lineplus(const int & l , const int &r, const int &k) {
23     add(l , k);
24     add(r + 1 , -k);
25 } //区间修改 & 单点查询 //将区间l~~r的值加k , 要在差分数组中做 ,此时a为差分数组 
26 
27 int main() {
28     scanf("%d%d",&n,&m);
29     for(int i = 1; i <= n; i++) {
30         scanf("%d",&a[i]);
31         add(i , a[i]);
32         //add(i, a[i] - a[i-1]) //差分 
33     }
34     int x,y;
35     for(int i = 1; i <= m; i++) {
36         scanf("%d%d",&x,&y);
37         printf("%d\n",query(y) - query(x - 1));//求的是区间和(x~~y)
38     }
39 /*  int k;
40     scanf("%d%d%d",&x,&y,&k);
41     lineplus(x,y,k);//x~~y区间加k
42     
43     scanf("%d",&x) 
44     printf("%d",queue(x) );//查询单点x //差分得出的t的前缀和 
45 */
46 }

 

 

 逆序对:

洛谷之逆序对毒瘤Long Long 之 旅

注意数据范围

#include <cstdio>
#include <algorithm>

#define MAXN 222222

using namespace std;

int n, ans, tot;
int a[MAXN], b[MAXN], t[MAXN];//t为树状数组,b用来离散化a

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

int query(int x) {
    int s = 0;
    for(int i = x; i; i -= lowbit(i)) s += t[i];
    return s;
}//区间查询 返回的是前缀和

void add(int x) {
    for(int i = x; i <= n; i += lowbit(i)) t[i]++;
}

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];//b用于去重

    //离散化:将数值转化为排名,减小取值范围
    sort(b + 1, b + 1 + n);//先升序排个序
    tot = 1;//第一个是不用去重的
    printf("去重前 ");
    for(int i = 1; i <= n; i++) printf("%d ", b[i]);//
    for(int i = 2; i <= n; i++) if(b[i] != b[tot]) b[++tot] = b[i];//去重 ,从第二个开始
    //补:unique()函数是一个去重函数,功能是去除相邻的重复元素(只保留一个)
    //unique(num,mun+n)返回的是num去重后的尾地址,
    printf("\n去重后 ");
    for(int i = 1; i <= tot; i++) printf("%d ", b[i]);
    for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
    //lower_bound(b + 1, b + 1 + tot, a[i])是在区间b + 1~~~b + 1 + tot中找到第一个大于等于a[i]的数,并返回它的地址
    //所以要“- b”减b ,之后,返回的是值等于a[i]的下标(即排名) 因为b一定有a[i]  所以它就是找第一个等于a[i]的数
    printf("\n离散化a  ");
    for(int i = 1; i <= n; i++) printf("%d ", a[i]);

    for(int i = 1; i <= n; i++) {
        add(a[i]);
    /*
        add(a[i]) 的意思是在位置a[i]有了一个数,
        所以说下面求的query(a[i])就是 i前面比a[i]大的数的个数(因为你是从1~n依次往树状数组里加的,对于位置i,你肯定只加了1~i位置上的数 
        因为 a[i] 的数据范围是1~500000,所以你需要开个50W的树状数组 t[],在这样的数据下内存会炸的,所以需要离散化 
    */
        int tmp = query(a[i]);
        /*
        逆序对求的是第i个数之前大于 a[i] 的数的个数
        不妨用树状数组求前缀和的方法求 [1,i]中 <= a[i] 的数的个数(一开始47的tmp) 
        然后 第i个数前的逆序对即为 i - tmp 
        */
        tmp = i - tmp;
        ans += tmp;//逆序对的个数的和
    }
    printf("\n%d\n", ans);
    return 0;
}
/*
7
6 2 5 1 3 7 4
7
33 23 17 999 5 17 999
*/

 

 

 

前缀和(二维):

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 
 5 #define MAXN 1111
 6 #define MAXM 1111
 7 
 8 int n,m;//长&宽
 9 int a[MAXN][MAXM],s[MAXN][MAXM];
10 /*a为矩阵每个点的值,s[i][j]表示 以[i,j]为右下角的矩阵中(左上角为(0,0))
11 所有方格中数的和
12 */
13 int x1,y1,x2,y2;
14 /*询问的是以(x1,y1)为左上 以(x2,y2)为右下的矩阵中
15 所有方格中数的和
16 */
17 
18 int main() {
19     scanf("%d%d",&n,&m);
20     for(int i = 1; i <= n; i++) {
21         for(int j = 1; j <= m; j++) {
22             scanf("%d",&a[i][j]);
23         }
24     }
25     //初始化:
26     for(int i = 1; i <= n; i++) {
27         for(int j = 1; j <= m; j++) {
28             s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
29             //        (都是已知的)           s[i - 1][j - 1]为重复加的  然后加上他自己
30         }
31     }
32     scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
33     printf("%d",s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]);
34     // s[x1 - 1][y1 - 1] 重复减的
35     //温馨提示:画个矩阵呗
36 }

 

区间加:

给定一个序列a(初值全为0)。有很多次操作:
将a[I ~~ j]的每个值加上k
询问a[x]的值
(实际上是区间修改,单点查询问题,不过可以用差分数组改成区间查询(求p[i]的前缀和)单点修改(修改p[l],p[r + 1]的值)
差分数组的前缀和即为原数组,修改同步

代码:

 1 #include<cstdio>
 2            /* 5   2  4  4  3
 3            p:5  -3 -2  0  -1 */
 4 #define MAXN 1111
 5            int n,m;//给定序列a的长度为n,m次操作
 6            int a[MAXN],p[MAXN];//p 表示a中相邻元素的差 例:p[i] = a[i] - a[i - 1]
 7 //                 即差分
 8            int l,r,k;
 9 
10 void add(int l, int r, int k) { //加k的区间是【l , r】
11     p[l] += k;//p受影响的只有l 和 r+1 因为a[r + 1]值不变,所以p[r + 2]不变
12     p[r + 1] -= k;//只需要修改p[l] 和 p[r + 1] 的值,然后求一个前缀和就行了
13 }
14 
15 void get_a() {
16     for(int i = 1; i<= n; i++) {
17         a[i] = a[i - 1] + p[i];//相当于一维前缀和
18     }
19 }
20 
21 int main() {
22     scanf("%d%d",&n,&m);
23     for(int i = 1; i <=m; i++) {
24         scanf("%d %d %d",&l,&r,&k);
25         add(l,r,k);
26     }
27     get_a();
28     for(int i = 1; i <=n; i++) {
29         printf("%d ",a[i]);
30     }
31 }

 

转载于:https://www.cnblogs.com/tyner/p/10801626.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值