权值线段树和权值树状数组一般是将权值离散化后做当下标大小建立的
可以很方便的处理与数值大小有关的查询
权值树状数组很多时候要要+一个二分查找
时间复杂度差不多,线段树常数较大,哪怕树状数组+一个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)