zoukankan      html  css  js  c++  java
  • 序列算法(树状数组在这儿!

    ps: 这是我刚学oi时写的笔记,代码超丑,但把萌新的错误点都标了出来,所以应该会比较友好...吧

    那我就不删咯~  ~~绝不是因为懒得重新编辑成markdown~~

    求区间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
    ", 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
    ",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 }

     区间修改区间查询

    ~~要不咱写线段树?~~, 我先说明,这是我后来补上的,刚学树状数组时没学这个。

    参见这篇博文

    她(应该是个妹子)写的比我不知道高到哪里去了....

    例题:

    树状数组 3 :区间修改,区间查询

    由于这个编辑器不适合写Latex公式,同学们可以去我的另外一篇关于基础算法的博文里学习。

     逆序对:

    洛谷之逆序对毒瘤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("
    去重后 ");
        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("
    离散化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("
    %d
    ", 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 }
  • 相关阅读:
    jQuery-1.9.1源码分析系列(十六)ajax——ajax框架
    js基础篇——encodeURI 和encodeURIComponent
    apache分析之三、配置文件解析
    apache分析之二、post-commit后台运行
    apache分析之一、svn:Could not open the requested SVN filesystem
    从例子看git的内部实现
    stl中容器的end指向哪里
    使用dependency walker定位windows下程序启动失败原因
    lvs中的保活、persistent及DR
    当磁盘空间满之后,为什么文件依然可以被创建
  • 原文地址:https://www.cnblogs.com/tyner/p/10801626.html
Copyright © 2011-2022 走看看