题目链接
题意:给一个序列,序列里面每个值的的含义是该位置奶牛前面比它高的奶牛数,求该奶牛的身高。
思路:
①线段树:可以看到1在的位置一定的数一定是0,而选出1后,2就是最小的了。用线段树维护这个序列,每次查询出区间最小数的位置。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN = 8005;
const int INF = 0x3f3f3f3f;
int a[MAXN], ans[MAXN];
int n;
struct Tree{ //维护最小值和最小值位置的线段树,支持区间加减,查询只要tree[1].minpos即可
int minn;
int minpos;
int tag;
}tree[MAXN*4];
void pushUp(int node){
if(tree[node*2].minn < tree[node*2+1].minn){
tree[node].minn = tree[node*2].minn;
tree[node].minpos = tree[node*2].minpos;
}
else{
tree[node].minn = tree[node*2+1].minn;
tree[node].minpos = tree[node*2+1].minpos;
}
}
void pushDown(int node){
int &tag = tree[node].tag;
if(tag != 0){
tree[node*2].minn += tag;
tree[node*2].tag += tag;
tree[node*2+1].minn += tag;
tree[node*2+1].tag += tag;
tag = 0;
}
}
void build(int node, int l, int r){
tree[node].tag = 0;
if(l == r){
tree[node].minn = a[l];
tree[node].minpos = l;
return;
}
int mid = (l+r) / 2;
build(node*2, l, mid);
build(node*2+1, mid+1, r);
pushUp(node);
}
void update(int node, int L, int R, int l, int r, int v){
if(L >= l && R <= r){
tree[node].minn += v;
tree[node].tag += v;
return;
}
pushDown(node);
int mid = (L+R) / 2;
if(r <= mid) update(node*2, L, mid, l, r, v);
else if(l > mid) update(node*2+1, mid+1, R, l, r, v);
else{
update(node*2, L, mid, l, mid, v);
update(node*2+1, mid+1, R, mid+1, r, v);
}
pushUp(node);
}
int main()
{
cin >> n;
a[1] = 0;
for(int i = 2; i <= n; ++i)
cin >> a[i];
build(1, 1, n);
for(int i = 1; i <= n; ++i)
{
int pos = tree[1].minpos;
ans[pos] = i;
update(1, 1, n, pos, pos, INF);
a[pos] = INF;
if(pos < n) update(1, 1, n, pos+1, n, -1);
}
for(int i = 1; i <= n; ++i)
cout << ans[i] << endl;
return 0;
}
②树状数组:一个奶牛,若前面有x个比它高的,则它是第x+1高的,且排除后面已经选出的部分。用树状数组维护一个01序列,每次找到值为x+1的lower_bound,再把这个位置的0变成1,这样后面在计算前缀和的时候就不相当于跳过此值。
是一个非常巧妙的做法
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define lowbit(i) ((i) & (-i))
const int MAXN = 8005;
int n;
int a[MAXN], ans[MAXN];
int c[MAXN];
void update(int x, int v){
for(int i = x; i <= n; i = i+lowbit(i)){
c[i] += v;
}
}
int getSum(int x){
int sum = 0;
for(int i = x; i > 0; i = i-lowbit(i)){
sum += c[i];
}
return sum;
}
int getPos(int k){ //找到前缀和为k的lower_bound
int l = 1, r = n;
while(l <= r){
int mid = (l+r) / 2;
int res = getSum(mid);
if(res < k){
l = mid+1;
}
else if(res > k){
r = mid-1;
}
else{
r = mid-1;
}
}
return l;
}
int main()
{
cin >> n;
a[1] = 0;
for(int i = 2; i <= n; ++i)
cin >> a[i];
for(int i = 1; i <= n; ++i)
update(i, 1);
for(int i = n; i >= 1; --i)
{
int rank = a[i]+1;
int pos = getPos(rank);
ans[i] = pos;
update(pos, -1);
}
for(int i = 1; i <= n; ++i)
cout << ans[i] << endl;
return 0;
}