树状数组

一、树状数组单点更新与区间查询模板

 1 //求出最低位的1
 2 int lowbit(int x)
 3 {
 4     return x & (-x);
 5 }
 6 
 7 //单点更新
 8 void update(int x,int y,int n)
 9 {
10     for(int i = x;i <= n;i += lowbit(i))
11         c[i] += y;
12 }
13 
14 //区间查询(求和)
15 int query(int x)
16 {
17     int ans = 0;
18     for(int i = x;i >= 1;i -= lowbit(i))
19         ans += c[i];
20     return ans;
21 }

模板例题:敌兵布阵

http://acm.hdu.edu.cn/showproblem.php?pid=1166

题意:T组测试用例,给出n个营地的人数,将会询问多次,输入一个字符串,如果是Query就输出i到j间的总人数,如果是Add则在第i个兵营增加j个人,如果是Sub则第i个兵营减少j人,如果是End则询问结束。

AC代码

 1 #include <bits/stdc++.h>
 2 #define INF 50000
 3 
 4 using namespace std;
 5 
 6 int t;
 7 int n;
 8 char ch[10];
 9 int c[4*INF + 1];
10 
11 //求出最低位的1
12 int lowbit(int x)
13 {
14     return x & (-x);
15 }
16 
17 //单点更新
18 void update(int x,int y,int n)
19 {
20     for(int i = x;i <= n;i += lowbit(i))
21         c[i] += y;
22 }
23 
24 //区间查询
25 int query(int x)
26 {
27     int ans = 0;
28     for(int i = x;i >= 1;i -= lowbit(i))
29         ans += c[i];
30     return ans;
31 }
32 
33 int main()
34 {
35     scanf("%d",&t);
36     for(int p = 1;p <= t;p++)
37     {
38         scanf("%d",&n);
39         memset(c,0,sizeof(c));
40         for(int i = 1;i <= n;i++)
41         {
42             int x;
43             scanf("%d",&x);
44             update(i,x,n);
45         }
46         printf("Case %d:\n",p);
47         while(1)
48         {
49             int l,r;
50             int ans = 0;
51             scanf("%s",ch);
52             if(ch[0] == 'E')
53                 break;
54             scanf("%d%d",&l,&r);
55             if(ch[0] == 'Q')
56             {
57                 ans = query(r) - query(l - 1);
58                 printf("%d\n",ans);
59             }
60             else if(ch[0] == 'A')
61             {
62                 update(l,r,n);
63             }
64             else if(ch[0] == 'S')
65             {
66                 update(l,-r,n);
67             }
68         }
69     }
70     return 0;
71 }

二、树状数组求逆序对

  首先了解什么是逆序对:设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。

模板:

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

void update(int x,int y)
{
    for(int i = x;i <= n;i += lowbit(i))
        a[i] += y;
}

int query(int x)
{
    int ans = 0;
    for(int i = x;i >= 1;i -= lowbit(i))
        ans += a[i];
    return ans;
}

模板例题:Minimum Inversion Number

http://acm.hdu.edu.cn/showproblem.php?pid=1394

题意:给出一个长度为n的数组,分别经过n次转换,让你求出n次转换中数组的逆序对的最小值;

转换演示:

a1, a2, ..., an-1, an (第一次不变)
a2, a3, ..., an, a1 (第二次)
a3, a4, ..., an, a1, a2 (第三次)
...
an, a1, a2, ..., an-1 (第n次)

AC代码:

 1 #include <bits/stdc++.h>
 2 #define inf 5000
 3 
 4 using namespace std;
 5 
 6 int n;
 7 int a[inf * 4];
 8 int b[inf * 4];
 9 long long sum = 0;
10 long long ans = 0x7f7f7f7f;
11 
12 int lowbit(int x)
13 {
14     return x & -x;
15 }
16 
17 void update(int x,int y)
18 {
19     for(int i = x;i <= n;i += lowbit(i))
20         a[i] += y;
21 }
22 
23 int query(int x)
24 {
25     int ans = 0;
26     for(int i = x;i >= 1;i -= lowbit(i))
27         ans += a[i];
28     return ans;
29 }
30 
31 int main()
32 {
33     while(scanf("%d",&n) != EOF)
34     {
35         memset(a,0,sizeof(a));
36         sum = 0;
37         for(int i = 1;i <= n;i++)
38         {
39             scanf("%d",&b[i]);
40             b[i]++;
41             sum += query(n) - query(b[i]);
42             update(b[i],1);
43         }
44         ans = sum;
45         for(int i = 1;i <= n;i++)
46         {
47             sum += n - b[i] - (b[i] - 1);
48             if(sum < ans)
49                 ans = sum;
50         }
51         printf("%lld\n",ans);
52     }
53     return 0;
54 }

 

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

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

void update(int x)
{
    for(;x <= n;x += lowbit(x))
    {
        a[x] = b[x];
        for(int i = 1;i < lowbit(x);i += lowbit(i))
            a[x] = max(a[x],a[x - i]);
    }
}

int query(int l,int r)
{
    int ans = 0;
    while(r >= l)
    {
        ans = max(ans,b[r]);
        r--;
        for(;r - lowbit(r) >= l;r -= lowbit(r))
            ans = max(ans,a[r]);
    }
    return ans;
}

模板例题:I Hate It

http://acm.hdu.edu.cn/showproblem.php?pid=1754

 

题意:给出长度为n的数组,进行m次询问,输入字符为Q时,输出i到j区间的最大值,如果输入字符为U时,将第i位数修改为j

AC代码:

#include <bits/stdc++.h>
#define INF 200001

using namespace std;

int a[INF];
int b[INF];
int m,n;
int l,r;
int x;
string s;

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

void update(int x)
{
    for(;x <= n;x += lowbit(x))
    {
        a[x] = b[x];
        for(int i = 1;i < lowbit(x);i += lowbit(i))
        a[x]=max(a[x],a[x - i]);
    }
    return ;
}

int query(int l,int r)
{
    int ans = 0;
    while(r >= l)
    {
        ans = max(ans,b[r]);
        r--;
        for(;r - lowbit(r) >= l;r -= lowbit(r))
        ans=max(ans,a[r]);
    }
    return ans;
}

int main()
{
    while(scanf("%d",&n) != EOF)
    {
        scanf("%d\n",&m);
        memset(a,0,sizeof(a));
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&b[i]);
            update(i);
        }
        while(m--)
        {
            cin >> s;
            if(s[0] == 'Q')
            {
                scanf("%d%d",&l,&r);
                printf("%d\n",query(l,r));
            }
            else
            {
                scanf("%d%d",&l,&r);
                b[l] = r;
                update(l);
            }
        }
    }
    return 0;
}

 

posted @ 2019-04-23 16:23  _Fancy  阅读(123)  评论(0编辑  收藏  举报