大牛博客 这个题我是参考了我斌巨的博客,写的很不错!
题意: 给出n和每个数之前比它小的数有几个 需要输出原序列
思路:
首先,我们需要明确,给定一个逆序数的话我们最后一个数总是可以确定出来的,他就是这个序列中第a[i]+1大的数
那么我们确定这个数后把这个数删除,那么倒数第二个数就是剩下的是中第x+1大的数.
1.二分+树状数组
初始化 每个位置的下标代表牛,都设置成1 代表有一个此牛,如果找到某一个位置的编号 就把该位置清0 维护树状
数组.
我们都知道树状数组可以求逆序数,那么我们从最后一个位置往前开始,二分查找牛的编号,并求出当前位置有
多少个比他小的(sum(x)-1)和a[i]比较,然后将该位置的清0
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=1e4;
typedef long long ll;
int s[maxn],a[maxn],ans[maxn];
int n;
int lowbit(int x)
{
return x&-x;
}
void add(int x,int num)
{
while(x<maxn)
{
s[x]+=num;
x+=lowbit(x);
}
return ;
}
int sum(int x)
{
int ss=0;
while(x>0)
{
ss+=s[x];
x-=lowbit(x);
}
return ss;
}
int check(int x)
{
int pos;
int l=1,r=n,mid;
while(l<=r)
{
mid=(l+r)/2;
int num=sum(mid);
if(num-1>=x)
{
pos=mid;
r=mid-1;
}
else
{
l=mid+1;
}
}
return pos;
}
int main()
{
cin>>n;
add(1,1);//第一个位置单独 的初始化..
a[0]=0;
for(int i=1;i<n;i++)
{
scanf("%d",&a[i]);
add(i+1,1);
}
for(int i=n-1;i>=0;i--)
{
ans[i]=check(a[i]);
add(ans[i],-1);.//清0
}
for(int i=0;i<n;i++)
printf("%d\n",ans[i]);
return 0;
}
线段树:
t[d]的值表示当前区间内还有多少位置没确定,当我们确定了某个位置的时候就把它从线段树中删除,
对于当前的a[i]+1,如果左子树中未删除的元素满足第a[i]+1的话 就递归左子树 否则就递归右子树,但是注意递归右子树
时应满足 a[i]+1-t[2*d]个
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=8e3+10;
int t[4*maxn],a[maxn],ans[maxn];
int n;
void build(int l,int r,int d)
{
t[d]=r-l+1;//每一层上有多少个满足的
if(l==r)
return ;
int mid=(l+r)>>1;
build(l,mid,2*d);
build(mid+1,r,2*d+1);
return ;
}
int query(int l,int r,int d,int pos)
{
t[d]--;//这一层肯定必填 要删除
if(l==r)
return l;
int mid=(l+r)/2;
if(pos<=t[2*d])
return query(l,mid,2*d,pos);
else
return query(mid+1,r,2*d+1,pos-t[2*d]);//左子树不满足我们就查找右子树,但是要减去左子树所包含的...
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
a[0]=0;
for(int i=1;i<n;i++)
scanf("%d",&a[i]);
build(1,n,1);
memset(ans,0,sizeof(ans));
for(int i=n-1;i>=0;i--)
{
ans[i]=query(1,n,1,a[i]+1);
}
for(int i=0;i<n;i++)
printf("%d\n",ans[i]);
}
return 0;
}