树状数组的应用

具体定义和性质见蓝皮书P202页(有时间再添过来)
印象笔记可能更好看一点:https://app.yinxiang.com/fx/c...

树状数组的应用

树状数组与逆序对

当然归并排序也可以求逆序对
树状数组求逆序对时间复杂度为O((N+M)logM)----M为数据范围
归并排序求逆序对的时间复杂度为O(Nlogn)
PS:
归并排序就是每次把序列二分,递归对左右两半排序,然后合并两个有序序列
归并排序求逆序对就是递归对左右两半排序时,可以把左右两半各自内部的逆序对数作为子问题计算,因此我们只需要在合并时考虑"左边一半里一个较大的数“与“右边一半里一个较小的数”构成逆序对的情形,求出这种情形的个数。合并两个有序序列A[l~mid]与A[mid+1~r]可以采用两个指针i和j分别对二者进行扫描的方式,不断比较两个指针所指向的数值a[i]和a[j]的大小,将小的那个加入到排序的结果数组中。若小的是a[j],则a[i~mid]都比a[j]要大,他们都会与a[j]构成逆序对,顺便统计到答案中。
如果数据范围较大,即M较大,可以对序列进行离散化处理,不过离散化需要排序,不如直接用归并排序求。
题目链接:https://www.acwing.com/proble...
分析:
以找'V'标志为例:
树状数组的应用_第1张图片
AC代码:

#include 
#include 
#include 
#include 

using namespace std;
const int N = 200010;
int n;
int a[N];
long long int w1[N],w2[N],w3[N],w4[N];
long long int c[N];
long long int ask(int x)//查询操作
{
    long long int ans = 0;
    for(int i=x;i;i-=(i&-i))
    {
        ans += c[i];
    }
    return ans;
}
void add(int x)//添加操作
{
    for(;x<=n;x+=(x&-x))
    {
        c[x]+=1;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        w1[i]=ask(n)-ask(a[i]);//利用前缀和思想求出区间[1,i-1]中[a[i]+1,n]范围内的数
        w3[i]=ask(a[i]-1);//利用前缀和思想求出区间[1,i-1]中[1,a[i]-1]范围内的数
        add(a[i]);
    }
    memset(c,0,sizeof(c));
    for(int i=n;i>=1;i--)
    {
        w2[i]=ask(n)-ask(a[i]);
        w4[i]=ask(a[i]-1);
        add(a[i]);
    }
    long long int ans1 = 0,ans2=0;
    for(int i=2;i<=n-1;i++)
    {
        ans1+=w1[i]*w2[i];
        ans2+=w3[i]*w4[i];
    }
    printf("%lld %lld\n",ans1,ans2);
    return 0;
}

下一个问题:树状数组的扩展应用,题目链接:https://www.acwing.com/proble...
树状数组仅支持单点增加和区间查询
但本题问的是单点查询和区间增加,所以我们就要转化一下。
考虑差分的思想,新建一个数组b[],初始全部为0,对于每条“C l r d”指令,b[l]加上d,b[r+1]减去d(如果不明白差分,可以看这个https://app.yinxiang.com/fx/0...),这样区间查询时就可以变成单点查询。
举个例子
初始状态:
树状数组的应用_第2张图片

#include 
#include 
#include 
#include 

using namespace std;
const int N = 100010;
int a[N], b[N];
int n,m;
char ch;
int l,r,d;
int ask(int x)
{
    int ans = 0;
    for(;x;x-=(x&-x))
    {
        ans+=b[x];
    }
    return ans;
}
void add(int x,int d)
{
    for(;x<=n;x+=(x&-x))
    {
        b[x]+=d;
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    while (m--)
    {
        getchar();
        scanf("%c",&ch);
        if(ch=='Q')
        {
            scanf("%d",&l);
            printf("%d\n",a[l]+ask(l));
        }
        else
        {
            scanf("%d %d %d",&l,&r,&d);
            add(l,d);
            add(r+1,-d);
        }
    }
    
    return 0;
}

你可能感兴趣的:(树状数组)