zoukankan      html  css  js  c++  java
  • 树状数组与离散化

    https://vjudge.net/contest/262670#problem/B

    线段树

    https://vjudge.net/contest/230809#problem/T

    题目链接。后面两题比较难。

    add与sum操作的时间复杂度都是logn的,(和二分有点相似,在时间复杂度方面),如果预处理执行n次add操作的话就是nlogn的复杂度。

    https://blog.csdn.net/qq_37685156/article/details/79822314

    树状数组,乒乓球比赛类似于求逆序数,好像可以用归并法来做,训练之南上的

    https://blog.csdn.net/lin375691011/article/details/21247409

    二维树状数组

    https://www.cnblogs.com/whatbeg/p/3970310.html

    三维树状数组,如果是01取反的话,在1的基础上加1与-1是一样的。

    二维区间修改的图。

     Matrix

     POJ - 2155

    差分

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    int T,n,m,bit[1005][1005];
    char s[10];
    int sum(int a,int b) {
        int s = 0;
        for(int i=a;i>0;i-=i&-i)
            for(int j=b;j>0;j-=j&-j)
            s+=bit[i][j];
        return s;
    }
    void add(int a,int b) {
        for(int i=a;i<=n;i+=i&-i)
            for(int j=b;j<=n;j+=j&-j){
                bit[i][j]++;
            }
    }
    int main() {
        scanf("%d",&T);
        while(T--) {
            scanf("%d%d",&n,&m);
            memset(bit,0,sizeof(bit));
            while(m--) {
                scanf("%s",s);
                if(s[0]=='C'){
                    int x1,y1,x2,y2;
                    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                    add(x2+1,y2+1);
                    add(x1,y1);
                    add(x1,y2+1);
                    add(x2+1,y1);
                }
                else {
                    int x,y;
                    scanf("%d%d",&x,&y);
                    printf("%d
    ",sum(x,y)%2);
                }
            }
            if(T) printf("
    ");
        }
        return 0;
    
    }

    https://blog.csdn.net/qq_39826163/article/details/89450013

    【题意】

    在一个面积不超过n*m的矩形上,有p个矩形A,问之后的q个矩形B能否被之前的A全部覆盖(每个B的点都在至少一个A中)。

    【解题思路】

    对于A类矩形(x1,y1,x2,y2),我们只需要在(x1,y1),(x2+1,y2+1)处+1,在(x1,y2+1)(x2+1,y1)处-1。

    之后对整个面积求一个前缀和。则大于0的地方就是被A类矩形覆盖的点。 把值大于0的地方变成1,再一次求一次前缀和,处理好后即可在O(1)的时间算出一个矩形内被覆盖的点的数量。

    需要注意的是因为n*m<10^7,所以需要把二维转换成一维来做。
    重点是把二维的树状数组转成一维的

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn=1e7+500;
    LL a[maxn],s[maxn];
    int n,m,p,q;
    int lowbit(int x)
    {
        return (-x)&x;
    }
    void add(int x,int y,int z)
    {
        int ty=y;
        while(x<=n)
        {
            y=ty;
            while(y<=m)
            {
                a[x*m+y]+=z;
                y+=lowbit(y);
            }
            x+=lowbit(x);
        }
    }
    LL query(int x,int y)
    {
        LL sum=0;
        int ty=y;
        while(x)
        {
            y=ty;
            while(y)
            {
                sum+=a[x*m+y];
                y-=lowbit(y);
            }
            x-=lowbit(x);
        }
        return sum;
    }
    int main()
    {
        while(~scanf("%d%d",&n,&m))
        {
            memset(a,0,sizeof(a));
            memset(s,0,sizeof(s));
             int x1,y1,x2,y2;
             scanf("%d",&p);
             while(p--)
             {
                 scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                 add(x1,y1,1);
                 add(x1,y2+1,-1);
                 add(x2+1,y1,-1);
                 add(x2+1,y2+1,1);
             }
             for(int i=1;i<=n;i++)
             {
                 for(int j=1;j<=m;j++)
                 {
                     int t=query(i,j);
                     if(t>0)s[i*m+j]=1;
                     else s[i*m+j]=0;
                 }
             }
             for(int i=1;i<=n;i++)
             {
                 for(int j=1;j<=m;j++)
                    s[i*m+j]+=s[(i-1)*m+j]+s[i*m+j-1]-s[(i-1)*m+j-1];
             }
             scanf("%d",&q);
             while(q--)
             {
                 scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                 LL t1=(x2-x1+1)*(y2-y1+1);
                 LL t2=s[x2*m+y2]+s[(x1-1)*m+y1-1]-s[(x1-1)*m+y2]-s[x2*m+y1-1];
                 if(t1==t2)printf("YES
    ");
                 else printf("NO
    ");
     
             }
        }
    }

    https://wenku.baidu.com/view/402b20d0162ded630b1c59eef8c75fbfc67d9479.html

    浅谈信息学竞赛中的0和1.

    https://blog.csdn.net/getsum/article/details/51316927

    一个异或的题。

    https://blog.csdn.net/qq_39562952/article/details/81298043

    区间更新与区间查询。

    https://blog.csdn.net/weizhuwyzc000/article/details/45442815/

    二维的区间更新,与一维有点不同。

    查找第几个比i大的值可以用二分。注意判断某一位是否为零必须是sum[i] - sum[i - 1] == 0 而不是直接判那一位是否为零,因为bit[i]指的是他管辖的范围内的值得总和。

    注意当给一个矩形对角线坐标时,要判大小,小的放在一起,大的放在一起(可能会要换一条对角线,为了让二维树状数组好算)。

    一般为了防止为零都会加一。

    有时候要把sum的值用long long 存。

    离散化后add是就是n,没离散化就是maxm.

    用二分的时候范围记得搞大点。

    1.预处理,输入并离散化处理 

    void init()
    {
    for(int i=1;i<=n;i++)//输入n个数(1-n) 
    {
    scanf("%d",&d[i].v);
    d[i].ord=i;
    }
    sort(d+1,d+n+1,cmp1);
    for(int i=1;i<=n;i++)
    {
    b[d[i].ord]=i;
    }
    
    }

     成对数据离散化

    struct node {
    ll x, y, xx, yy;
    } p[maxm];
    
    sort(p + 1, p + 1 + n, comp1);
        p[1].x = 1;
        for(int i = 2; i <= n; i++) {
            if(p[i].xx == p[i - 1].xx) {
                p[i].x = p[i - 1].x;
            }
            else p[i].x = i;
        }
        sort(p + 1, p + n + 1, comp2);
        p[1].y = 1;
        for(int i = 2; i <= n; i++) {
            if(p[i].yy == p[i - 1].yy) {
                p[i].y = p[i - 1].y;
            }
            else p[i].y = i;
        }

    离散化处理。

    2.一维前缀和与改变值

    int lowbit(int x) {
    return x & -x;
    }
    void add(int i, int x) {
    while(i <= 32005) {//如果离散化的话就是n,没离散化就是maxm.
        bit[i] += x;
        i += lowbit(i);
    }
    }
    int sum(int i) {
    int s = 0;
    while(i > 0) {
        s += bit[i];
        i -= i & -i;
    }
    return s;
    
    }
    //注意先sum后add或者先add后sum.

    3.二维

     Mobile phones

     POJ - 1195 

     

    int lowbit(int x) {
    
    return x & -x;
    }
    void add(int x, int y, int a) {
    for(int i = x; i <= n; i += lowbit(i)) {
        for(int j = y; j <= n; j += lowbit(j)) {
            bit[i][j] += a;
        }
    }
    }
    int sum(int x, int y) {
    int s = 0;
    for(int i = x; i > 0; i -= lowbit(i)) {
        for(int j = y; j > 0; j -= lowbit(j)) {
            s += bit[i][j];
        }
    }
    return s;
    }


    sum(l2, r2) + sum(l1 - 1, r1 - 1) - sum(l2, r1 - 1) - sum(l1 - 1, r2)//一个矩形

     三维线状数组

    int c[maxn][maxn][maxn];
    int lowbit(int x){
        return x&(-x);
    }
    void add(int x,int y,int z, int a) {
        for(int i=x;i<=maxn;i+=lowbit(i)) {
            for(int j=y;j<=maxn;j+=lowbit(j)) {
                for(int k=z;k<=maxn;k+=lowbit(k)) {
                    c[i][j][k] += a;
                }
            }
        }
    }
    int getsum(int x,int y,int z) {
        int sum=0;
        for(int i=x;i>0;i-=lowbit(i)) {
            for(int j=y;j>0;j-=lowbit(j)) {
                for(int k=z;k>0;k-=lowbit(k)) {
                    sum+=c[i][j][k];
                }
            }
        }
        return sum;
    }

    树状数组求区间最大值模板

    专题结束在补

    https://vjudge.net/contest/293001#problem/H  Gym - 100971H

    Description
    一个人想邀请k个朋友来做客,给第i个朋友打电话他会告诉一个[ai,bi]表示包括这个人自己在内有ai到bi个人去这个人就会去,每次打电话都是从第一个朋友开始按顺序打,叫够k个人就不打电话了,问对1~n中每个k要打多少个电话恰能邀请到k个人
    Input
    第一行一整数n表示朋友数量,之后n行两个整数ai和bi表示该朋友去做客对人数的要求(1<=n<=2e5,1<=ai<=bi<=n)
    Output
    输出n个数表示k从1取到n时至少需要打多少电话才能叫到k个人,如果给n个人打电话后也叫不齐k个人则输出-1
    Sample Input
    6
    3 3
    1 2
    3 6
    3 4
    1 4
    4 6
    Sample Output
    2 5 4 6 -1 -1
    解法 :树状数组,按区间值来排序,有点类似于差分,当邀一人时,就把所有可以为一的加到bit上去,两人时就接着邀。然后二分找值。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int maxm = 2e5 + 5;
    int bit[maxm];
    int n;
    int res[maxm];
    struct Node {
        int ind, v, flag;
        bool operator < (const Node &a) const {
            return v < a.v;
        }
        Node(int ind = 0, int v = 0, int flag = 0) : ind(ind), v(v), flag(flag) {};
    }node[maxm * 2];
    
    int lowbit(int x) {
        return x & -x;
    }
    
    void add(int x, int val) {
        while(x <= n) {
            bit[x] += val;
            x += lowbit(x);
        }
    }
    
    int sum(int x) {
        int s = 0;
        while(x > 0) {
            s += bit[x];
            x -= lowbit(x);
        }
        return s;
    }
    
    
    int main() {
        scanf("%d", &n);
        int ql, qr;
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", &ql, &qr);
            node[2 * i - 1] = Node(i, ql, 1);
            node[2 * i] = Node(i, qr + 1, -1);
        }
        sort(node + 1, node + 2 * n + 1);
        int r = 1;
        for(int i = 1; i <= n; i++) {
            while(r <= 2 * n && node[r].v <= i) {
                add(node[r].ind, node[r].flag);
                r++;
            }
            int ml = 1, mr = n, ans = n + 1, mid;
            while(ml <= mr) {
                mid = (ml + mr) >> 1;
                if(sum(mid) >= i) {
                    if(sum(mid) == i) {
                        ans = min(ans, mid);
                    }
                    mr = mid - 1;
                }
                else {
                    ml = mid + 1;
                }
            }
            res[i] = (ans == n + 1) ? -1 : ans;
        }
        for(int i = 1; i <= n; i++) {
            printf("%d%c", res[i], i == n ? '
    ' : ' ');
        }
        return 0;
    }

    Cows

     POJ - 2481这个在排序的时候要注意一下。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int bit[100005], n, res[100005];
    struct node{
    int l, r, index;
    } p[100005];
    
    int lowbit(int x) {
    return x & -x;
    }
    void add(int i, int x) {
    while(i <= 100005) {
        bit[i] += x;
        i += lowbit(i);
    }
    }
    int sum(int i) {
    int s = 0;
    while(i > 0) {
        s += bit[i];
        i -= lowbit(i);
    }
    return s;
    }
    int comp(node p1, node p2) {
    
    if(p1.r == p2.r) return p1.l < p2.l;
    return p1.r > p2.r;
    }
    int main() {
    while(~scanf("%d", &n) && n) {
        memset(res, 0, sizeof(res));
        memset(bit, 0, sizeof(bit));
        for(int i  = 1; i <= n; i++) {
            scanf("%d%d", &p[i].l, &p[i].r);
            p[i].l++;
            p[i].r++;
            p[i].index = i;
        }
        sort(p + 1, p + 1 + n, comp);
        res[  p[1].index ] = 0;
        add(p[1].l, 1);
        for(int i = 2; i <= n; i++) {
            if(p[i].l == p[i - 1].l && p[i].r == p[i - 1].r) { //如果区间相等
                res[ p[i].index ] = res[ p[i - 1].index ];
            }
            else {
                res[ p[i].index ] = sum(p[i].l);
            }
            add(p[i].l, 1);
        }
        for(int i = 1; i <= n; i++) {
            if(i == 1) {
                printf("%d", res[1]);
            }
            else printf(" %d", res[i]);
        }
        printf("
    ");
    }
    
    
    return 0;
    }

    Apple Tree

     POJ - 3321  涉及到dfs序的一道题目

    https://www.cnblogs.com/gj-Acit/p/3236843.html

    dfs序两个数组记录的是他子树节点的最大值与最小值。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    using namespace std;
    
    const int maxm = 1e5 + 5;
    int bit[maxm], in[maxm], out[maxm], n, m, fork[maxm], ans;
    
    vector< vector<int> >edge(maxm);
    
    int lowbit(int x) {
    return x & -x;
    }
    int sum(int i) {
    
    int s = 0;
    while(i > 0) {
            s += bit[i];
            i -= lowbit(i);
    
    
    }
    return s;
    }
    void add(int i, int x) {
    while(i <= maxm) {
        bit[i] += x;
        i += lowbit(i);
    }
    
    }
    void dfs(int u) {
        in[u] = ans;
        for(int i = 0; i < edge[u].size(); i++) {
            ans++;
            dfs(edge[u][i]);
        }
        out[u] = ans;
    }
    int main() {
    scanf("%d", &n);
    int a = 0, b = 0;
    ans = 1;
    for(int i = 1; i < n; i++) {
        scanf("%d%d", &a, &b);
        edge[a].push_back(b);
    }
    for(int i = 1; i <= n; i++) {
        fork[i] = 1;
        add(i, 1);
    }
    dfs(1);
    scanf("%d", &m);
    for(int i = 0; i < m; i++) {
        char ch[3];
        int t = 0;
        scanf("%s", ch);
        scanf("%d", &t);
        if(ch[0] == 'Q') {
            printf("%d
    ", sum(out[t]) - sum(in[t] - 1));
        }
        else {
            if(fork[t]) {
                add(in[t], -1);
            }
            else {
                add(in[t], 1);
            }
            fork[t] = !fork[t];
    
        }
    }
    
    
    
    return 0;
    }

     MooFest

     POJ - 1990 好题

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    typedef long long int64;
    const int MAXN = 20010;
    
    struct Node{
        int x;
        int v;
    };
    Node node[MAXN];
    int n;
    int treeCount[MAXN];
    int treeDis[MAXN];
    
    int comp(Node n1, Node n2) {
    return n1.v < n2.v;
    }
    int lowbit(int x){
        return x&(-x);
    }
    
    int64 getSum(int x , int arr[]){
        int64 sum = 0;
        while(x){
             sum += arr[x];
             x -= lowbit(x);
        }
        return sum;
    }
    
    void add(int x , int val , int arr[]){
        while(x <= MAXN){
             arr[x] += val;
             x += lowbit(x);
        }
    }
    
    int64 solve(){
        int64 ans = 0;
        int64 totalDis = 0;
        memset(treeCount , 0 , sizeof(treeCount));
        memset(treeDis , 0 , sizeof(treeDis));
        sort(node + 1 , node+n + 1, comp);
    
        for(int i = 1 ; i <= n ; i++){
            int64 count = getSum(node[i].x , treeCount);
            int64 dis = getSum(node[i].x , treeDis);
            // left
            ans += node[i].v*(count*node[i].x-dis);
            // right
            ans += node[i].v*(getSum(MAXN, treeDis)-dis-(i - 1-count)*node[i].x);
            // update
            totalDis += node[i].x;
            add(node[i].x , 1 , treeCount);
            add(node[i].x , node[i].x , treeDis);
        }
        return ans;
    }
    
    int main(){
        while(scanf("%d" , &n) != EOF){
             for(int i = 1 ; i <= n ; i++)
                 scanf("%d%d" , &node[i].v , &node[i].x);
             printf("%lld
    " , solve());
        }
        return 0;
    }

    KiKi's K-Number

     HDU - 2852 涉及二分,好题

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    typedef long long ll;
    const int maxm = 1e5 + 5;
    int n, T, x, a, b;
    ll bit1[maxm], bit2[maxm];
    
    int lowbit(int x) {
    return x & -x;
    }
    ll sum(int i, ll a[]) {
    ll s = 0;
    while(i) {
        s += a[i];
        i -= lowbit(i);
    }
    return s;
    }
    void add(int i, int x, ll a[]) {
    while(i <= maxm) {
        a[i] += x;
        i += lowbit(i);
    }
    
    }
    
    int main() {
    while(~scanf("%d", &T)) {
        memset(bit1, 0, sizeof(bit1));
    //    res = 0;
        for(int i = 1; i <= T; i++) {
            scanf("%d", &n);
            if(n == 0) {
                scanf("%d", &a);
                add(a, 1, bit1);
    //            printf("%d
    ", sum(maxm, bit1));
            }
            else if(n == 1) {
                scanf("%d", &a);
                if(sum(a, bit1) - sum(a - 1, bit1) == 0) {
                    printf("No Elment!
    ");
                }
                else {
                    add(a, -1, bit1);
                }
            }
            else {
                scanf("%d%d", &a, &b);
                if(sum(maxm, bit1) - sum(a, bit1) < b) {
                    printf("Not Find!
    ");
                }
                else {
                    int l = a, r = maxm + 1, res = 0, pp = a;;
                    while(l <= r) {
                        int mid = (l + r) / 2;
    //                    printf("%d
    ", mid);
                        if(sum(mid, bit1) - sum(pp, bit1) >= b){
                            r = mid - 1;
                            res = mid;
    //                        printf("%d
    ", sum(mid, bit1));
                        }
                        else {
                            l = mid + 1;
                        }
                    }
                    printf("%d
    ", res);
                }
            }
        }
    }
    return 0;
    
    }

     Necklace

     HDU - 3874 线段树离线操作

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    using namespace std;
    
    const int maxn = 1e6+10;
    typedef long long LL;
    int a[50000+10];
    LL ans[200000+10];
    LL s[maxn];
    int last[maxn];//记录值的位置
    int n,m;
    struct ss{
        int x,y,z;
        bool operator < (const ss &p){
        return y<p.y;
    }
    }q[200000+10];
    
    int lowbit(int x){
        return x&-x;
    }
    
    void add(int x, int v){
        while(x<=n){
            s[x] += v;
            x += lowbit(x);
        }
    }
    
    LL sum(int x){
        LL ans = 0;
        while(x>0){
            ans += s[x];
            x -= lowbit(x);
        }
        return ans;
    }
    
    int main(){
        int t;
        scanf("%d", &t);
        while(t--){
            memset(s,0,sizeof(s));
            memset(last,0,sizeof(last));
            scanf("%d", &n);
            for(int  i = 1; i <= n; ++i){
                scanf("%d",&a[i]);
                add(i,a[i]);
                if(!last[a[i]])
                    last[a[i]] = i;//初始化每个值第一次出现的位置
            }scanf("%d", &m);
            for(int i = 1; i <= m; ++i){
                scanf("%d %d", &q[i].x, &q[i].y);
                q[i].z = i;
            }sort(q + 1, q + 1 + m);
            int l = 1;
            for(int i = 1; i <= m; ++i){
                for(int j = l; j <= q[i].y ;++j)
                if(last[a[j]] != j){//如果值不在当前位置,那么就删去
                    add(last[a[j]], -a[j]);
                    last[a[j]] = j;//更新值为当前位置
                }
                l = q[i].y;
                 ans[q[i].z] = sum(q[i].y) - sum(q[i].x - 1);
            }
            for(int i = 1; i <= m; ++i)
                printf("%lld
    ",ans[i]);
        }
        return 0;
    }


    1:单点修改:

    
    
    void update(int x,int v)//单点修改(x节点加上v)
    {
        for(int i=x;i<=n;i+=lowbit(i))
            c[i]+=v;
    }
    
    

    2:前缀和:

    
    
    复制代码
    int sum(int x)//sum[1,x]
    {
        int ans=0;
        for(int i=x;i>=1;i-=lowbit(i))
            ans+=c[i];
        return ans;
    }
    复制代码
    
    

    3:区间修改,单点求值(差分思想)

    
    
    int solve(int l,int r,int v,int x)//[l,r]区间同时加上v,同时求x节点值(这时转化为求前缀和)
    {
        c[l]+=v;
        c[r+1]-=v;
        return sum(x);
    }
    
    

    4:区间修改,区间求和(差分思想,辅助数组)

    
    
    复制代码
    int solve(int l,int r,int v)//[l,r]同时增加v,同时求得[l,r]区间和
    {
        for(int i=1;i<=n;i++){
            cin>>a[i];
            update(diff,i,a[i]-a[i-1]);
            update(aux,i,(i-1)*(a[i]-a[i-1]));
        }
        l--;
        int sumr=r*sum(diff,r)-sum(aux,r);
        int suml=l*sum(diff,l)-sum(aux,l);
        return sumr-suml;
    }
    复制代码
    
    

    5:区间最大最小值

    
    
    复制代码
    void update(int x)//更改节点值
    {
        for(int i=x;i<=n;i+=lowbit(i)){//将所有x能影响到的节点更新
            h[i]=a[i];//首先取原数组值
            for(int j=1;j<lowbit(i);j<<=1){//在子节点中寻求最优值
                h[i]=max(h[i],h[i-j]);
            }
        }
    }
    int querymax(int l,int r)//区间最大值
    {
        int ans=0;
        while(r>=l){
            for(;r-lowbit(r)>=l;r-=lowbit(r))//找到该节点所示的区间包含于[x,y],可直接取根节点最优值
                ans=max(ans,h[r]);
            ans=max(ans,a[r]);//取当前区间右端点,下一步进行[x,y-1]区间寻找
            r--;
        }
        return ans;
    }
    复制代码
    
    

     6:二维树状数组

    
    
    复制代码
    void update(int x,int y,int v)//[x,y]值更新
    {
        for(int i=x;i<=n;i+=lowbit(i))
            for(int j=y;j<=m;j+=lowbit(j))
                c[i][j]+=v;
    }
    int sum(int x,int y)//[1,1]到[x,y]子矩阵前缀和
    {
        int ans=0;
        for(int i=x;i>=1;i-=lowbit(i))
            for(int j=y;j>=1;j-=lowbit(j))
                ans+=c[i][j];
        return ans;
    }
    int solve(int x1,int y1,int x2,int y2)//求该子矩阵区间和
    {   
        return sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1);
    }
    复制代码
     
  • 相关阅读:
    计算任一输入的正整数的各位数字之和,并分析算法的时间复杂度
    10万数组去重,排序,找最多出现次数,(复杂度没有前一个博客好,随手写,有点烂)
    Maven环境搭建
    Tomcat内部结构及请求原理(转)
    Tomcat环境搭建
    斐讯面试记录—三线程交替打印ABC
    斐迅面试记录—SSL和TLS的区别
    斐迅面试记录—Http协议中的Header
    斐讯面试记录—强+软+弱+虚引用
    斐讯面试记录—TCP滑动窗口及拥塞控制
  • 原文地址:https://www.cnblogs.com/downrainsun/p/10019964.html
Copyright © 2011-2022 走看看