权值线段树和权值树状数组一般是将权值离散化后做当下标大小建立的
可以很方便的处理与数值大小有关的查询
权值树状数组很多时候要要+一个二分查找
时间复杂度差不多,线段树常数较大,哪怕树状数组+一个logn
求逆序数:
朴素的逆序数算法:
#include
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
对于非常大的数,我们需要离散化,离散化并不会影响逆序对个数
#include
#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
题意:给定一个数组,问区间和
解析:
首先最a数组作前缀和,得c[i].
法1:主席树直接查询(i,n)小于等于m+c[i-1]-1的数目
#include
#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)
{
if(sum[y]-sum[x]==0) return 0;
if(l==r)
return sum[y]-sum[x];
ll mid=(l+r)>>1;
ll res=sum[L[y]]-sum[L[x]];
if(k<=mid)
query(L[x],L[y],l,mid,k);
else
return res+query(R[x],R[y],mid+1,r,k);
}
void init()
{
tot=0;
}
ll getnum(ll num,ll l,ll r,ll k)//查询小于等于k的数的个数
{
ll kk=lower_bound(b+1,b+num+1,k)-b;
if(b[kk]!=k)
kk--;
if(kk==0)
return 0;
else
return query(root[l-1],root[r],1,num,kk);
}
int main()
{
ll t,n,l,r,k;
ll m;
init();
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]),b[i]=b[i-1]+a[i],c[i]=c[i-1]+a[i];
sort(b+1,b+n+1);
ll num=unique(b+1,b+1+n)-b-1;
for(ll i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+num+1,c[i])-b;
}
root[0]=build(1,num);
for(ll i=1;i<=n;i++)
root[i]=update(root[i-1],1,num,a[i]);
ll ans=0;
for(ll i=1;i<=n;i++)
ans+=getnum(num,i,n,m+c[i-1]-1);
printf("%lld\n",ans);
return 0;
}
法2:同类似求逆序数的方法求
插入c[i-1],查询已经插入的数中大于等于[c[i]-m,n)的数目
ac:
#include
#define lowbit(x) (x)&(-x)
#define MAXN 200005
#define ll long long
using namespace std;
ll a[MAXN],b[MAXN],c[MAXN];
int sum[MAXN],n;
void add(ll x)
{
for(ll i=x;i<=n;i+=lowbit(i))
sum[i]+=1;
}
ll query(ll x)
{
ll ans=0;
for(ll i=x;i;i-=lowbit(i))
ans+=sum[i];
return ans;
}
int main()
{
ll m;
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),b[i]=b[i-1]+a[i],c[i]=a[i]=b[i];
b[n+1]=0;
n=n+1;
sort(b+1,b+n+1);
ll ans=0;
for(int i=1;i<=n-1;i++)
{
int g=lower_bound(b+1,b+n+1,a[i-1])-b;
add(g);
int k=(lower_bound(b+1,b+n+1,a[i]-m+1)-b-1);//注意这个-1是取开区间,一定要在已经离散化后往左移1位
ans+=i-query(k);
}
printf("%lld\n",ans);
return 0;
}
权值线段树:
这题的查询呈单调性可以不用主席树
类似主席树,但是省时间和空间
1.未将m+c[i-1]-1离散化,在查询的时候判断查询
ac:
#include
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
#define ll long long
#define MAXN 200005
using namespace std;
int sum[MAXN<<2];
ll a[MAXN],b[MAXN],c[MAXN];
void update(int v,int sign,int l,int r,int rt)
{
if(l==r){
sum[rt]+=sign;
return ;
}
int mid=(l+r)>>1;
if(v<=mid)
update(v,sign,ls);
else
update(v,sign,rs);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query(ll v,int l,int r,int rt)
{
if(b[r]<=v){
return sum[rt];
}
int mid=(l+r)>>1;
int ans=0;
if(v>=b[l])
ans+=query(v,ls);
if(v>b[mid])
ans+=query(v,rs);
return ans;
}
int main()
{
int n;
ll m;
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),a[i]=a[i-1]+a[i],c[i]=b[i]=a[i];
sort(b+1,b+n+1);
int num=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+num+1,a[i])-b;
update(a[i],1,1,num,1);
}
ll ans=0;
for(int i=1;i<=n;i++)
{
int kk=query(m+c[i-1]-1,1,num,1);
ans+=kk;
update(a[i],-1,1,num,1);
}
printf("%lld\n",ans);
return 0;
}
/*
5 4
5 -1 3 4 -1
*/
将m+c[i-1]-1与c[i]一起离散化创建权值线段树,方便查询,时间和空间复杂度比上面呢个更快,也更好处理
ac:
#include
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
#define ll long long
#define MAXN 400005
using namespace std;
int sum[MAXN<<2];
ll a[MAXN],b[MAXN],c[MAXN],q[MAXN];
void update(int v,int sign,int l,int r,int rt)
{
if(l==r){
sum[rt]+=sign;
return ;
}
int mid=(l+r)>>1;
if(v<=mid)
update(v,sign,ls);
else
update(v,sign,rs);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query(ll v,int l,int r,int rt)
{
if(r<=v){
return sum[rt];
}
int mid=(l+r)>>1;
int ans=0;
if(v>=l)
ans+=query(v,ls);
if(v>mid)
ans+=query(v,rs);
return ans;
}
int main()
{
int n;
ll m;
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),a[i]=a[i-1]+a[i],c[i]=b[i]=a[i];
for(int i=1;i<=n;i++)
q[i]=b[i+n]=m+c[i-1]-1;
sort(b+1,b+2*n+1);
int num=unique(b+1,b+2*n+1)-b-1;
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+num+1,a[i])-b;
q[i]=lower_bound(b+1,b+num+1,q[i])-b;
update(a[i],1,1,num,1);
}
ll ans=0;
for(int i=1;i<=n;i++)
{
ans+=query(q[i],1,num,1);
update(a[i],-1,1,num,1);
}
printf("%lld\n",ans);
return 0;
}
/*
5 4
5 -1 3 4 -1
*/
Governing sand
题意:
让你砍掉一些树后,剩下的树中,最高的树大于1半,砍掉没在树都有1定的代价
法1:(因为ci小,直接暴力)
#include
#define ll long long
#define MAXN 100005
using namespace std;
struct node
{
ll h,v,g;
friend bool operator< (node a,node b)
{
return a.hval[i])
{
x-=val[i];
sum+=val[i]*i;
}
else{
sum+=x*i;
break;
}
}
return sum;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;ipre)
{
maxs=min(maxs,cost);
}
else{
maxs=min(maxs,solve(pre-sum+1)+cost);
}
for(int j=last+1;j<=i;j++)
val[ee[j].v]+=ee[j].g;
last=i;
}
printf("%lld\n",maxs);
}
return 0;
}
法2:(权值线段树):(如果ci很大,就一定要用法2,法3)
#include
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define MAXN 100005
#define ll long long
using namespace std;
struct node
{
ll h,v,g;
friend bool operator <(node a,node b)
{
return a.h>1;
if(x<=m)
update(x,v,ls);
else
update(x,v,rs);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
num[rt]=num[rt<<1]+num[rt<<1|1];
}
ll query(ll k,ll l,ll r,ll rt)
{
if(l==r)
{
return l*k;
}
ll m=(l+r)>>1;
if(num[rt<<1]>=k)
return query(k,ls);
else
return sum[rt<<1]+query(k-num[rt<<1],rs);
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(sum,0,sizeof(sum));
memset(num,0,sizeof(num));
memset(hhv,0,sizeof(hhv));
memset(hhg,0,sizeof(hhg));
for(int i=1;i<=n;i++)
scanf("%lld%lld%lld",&ee[i].h,&ee[i].v,&ee[i].g);
sort(ee+1,ee+n+1);
for(int i=1;i<=n;i++)
{
hhg[i]+=hhg[i-1]+ee[i].g;
hhv[i]+=hhv[i-1]+ee[i].g*ee[i].v;
}
ll last=0,maxs=1e18;
for(int i=1;i<=n;i++)
{
if(ee[i].h==ee[i+1].h)
continue;
ll cot=hhv[n]-hhv[i];//后面树的花费
ll sum=hhg[i]-hhg[last];//这种高度的树的数目
if(sum>hhg[last])
{
maxs=min(maxs,cot);
}
else{
maxs=min(maxs,cot+query(hhg[last]-sum+1,1,200,1));
}
for(int j=last+1;j<=i;j++)
update(ee[j].v,ee[j].g,1,200,1);
last=i;
}
printf("%lld\n",maxs);
}
return 0;
}
法3:树状数组+二分
#include
#define lowbit(x) (x)&(-x)
#define ll long long
#define MAXN 100005
using namespace std;
struct node
{
ll h,v,g;
friend bool operator <(node a,node b)
{
return a.h0;i-=lowbit(i))
ans+=sum[i];
return ans;
}
ll getnum(ll r)//查询总数目
{
ll ans=0;
for(ll i=r;i>0;i-=lowbit(i))
ans+=Num[i];
return ans;
}
ll solve(ll xuyao)
{
ll l=1,r=200;
ll R1=0;
while(l<=r)
{
ll mid=(l+r)>>1;
if(getnum(mid)>=xuyao)//寻找符合条件的最小权值范围
{
r=mid-1;
R1=mid;
}
else{
l=mid+1;
}
}
ll ans1=getnum(R1);//砍去的树的数目
if(ans1>xuyao)
{
ll kk=getnum(R1-1);
return (xuyao-kk)*R1+query(R1-1);
}
else{
return query(R1);
}
}
int main()
{
ll n;
while(scanf("%lld",&n)!=EOF)
{
for(ll i=0;i=1;i--)
{
pass[i]+=pass[i+1]+ee[i].g*ee[i].v;
hh[i]+=hh[i+1]+ee[i].g;
}
ll maxs=1e18;
ll last=0,sum=0;
for(ll i=1;i<=n;i++)
{
if(ee[i+1].h==ee[i].h)
{
sum+=ee[i].g;
continue;
}
sum+=ee[i].g;
ll ans=pass[i+1];//比我高的价值和
ll xuyao=hh[1]-hh[last+1]-sum+1;
if(xuyao>0)
maxs=min(solve(xuyao)+ans,maxs);
else
maxs=min(maxs,ans);
for(ll j=last+1;j<=i;j++)
add(ee[j].v,ee[j].g);
last=i;
sum=0;
}
printf("%lld\n",maxs);
}
return 0;
}