权值树状数组&&权值线段树

博客探讨了权值树状数组和权值线段树在处理数值大小相关的查询时的作用,尤其是在计算逆序数和区间和问题上的应用。通过离散化技术,这两种数据结构可以有效地解决大规模数据问题。文章通过实例介绍了如何使用这些数据结构,并提供了AC代码,展示在不同场景下,如求逆序数和查询区间和,它们的效率和使用方式。此外,还提到了针对特定问题,如Governing sand,可以采用的优化策略,如权值线段树在处理代价问题上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

权值线段树权值树状数组一般是将权值离散化后做当下标大小建立的

可以很方便的处理与数值大小有关的查询

权值树状数组很多时候要要+一个二分查找

时间复杂度差不多,线段树常数较大,哪怕树状数组+一个logn

 

求逆序数

朴素的逆序数算法:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100000+10;
int a[maxn];
int main()
{
    memset(a,0,sizeof(a));
    int t,n;
    long long sum=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&t);
        sum =sum+ a[t];//如果出现了就加上去
        for(int j=0;j<t;j++)
            a[j]++;//比这个数小的就++,后面可能会用到.
    }
    printf("%lld\n",sum);
    return 0;
}

对于非常大的数,我们需要离散化,离散化并不会影响逆序对个数

#include<bits/stdc++.h>
#define ll long long
#define MAXN 200005
#define lowbit(x) (x)&(-x)
using namespace std;
ll sum[MAXN<<2];
int a[MAXN];
int b[MAXN];
int n,k;

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

ll query(int x)
{
    ll ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=sum[i];
    return ans;
}

int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for(int i=0;i<=2*n;i++)
            sum[i]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int cnt=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=n;i++)
            a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            add(a[i],1);
            ans+=i-query(a[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

https://codeforces.com/contest/1042/problem/D

题意:给定一个数组,问区间和<t的区间数目

解析:

首先最a数组作前缀和,得c[i].

法1:主席树直接查询(i,n)小于等于m+c[i-1]-1的数目

#include<bits/stdc++.h>
#define ll long long
#define MAXN 5000005
#define MAX 200005
using namespace std;
ll a[MAX],b[MAX],c[MAX];
ll root[MAX];
ll L[MAXN],R[MAXN];
ll sum[MAXN];
ll tot=0;

ll build(ll l,ll r)
{
    ll now=++tot;
    sum[now]=0;
    if(l!=r)
    {
        ll mid=(l+r)>>1;
        L[now]=build(l,mid);
        R[now]=build(mid+1,r);
    }
    return now;
}

ll update(ll pre,ll l,ll r,ll k)
{
    ll now=++tot;
    sum[now]=sum[pre]+1;
    L[now]=L[pre],R[now]=R[pre];
    if(l!=r)
    {
        ll mid=(l+r)>>1;
        if(k<=mid)
            L[now]=update(L[pre],l,mid,k);
        else
            R[now]=update(R[pre],mid+1,r,k);
    }
    return now;
}

ll query(ll x,ll y,ll l,ll r,ll k)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值