题目传送门
题意:
有n头牛,每头牛有一个序号(范围是1-n)。现在知道每头牛前面比自己序号小的牛的数量。需要求出每头牛的序号
分析:
可以想到的是,从后往前遍历。对于第i头牛,如果它前面有k头牛比它的序号小,那么它的序号就是当前仅剩编号中的第(k + 1)大的数。
暴力求法(类似约瑟夫环)
那么本题的难点就变成了如果求出剩下的编号中第m大的数,因为从后往前遍历的过程中,每确定一头牛的编号,那么剩下的编号就要去掉一个值。
这和约瑟夫环非常的类似(本题没有环),因此可以用vector模拟一个约瑟夫环来暴力求解
因为n = 8000,所以O(n ^ 2)的复杂度也勉强可以过
暴力代码:
//pre存储的是每头牛前面比自己序号小的牛的头数
int pre[N];
//r为剩下的编号的集合,ans为求出的每头牛的编号
VI r, ans;
int main(){
int n;
scanf("%d", &n);
for(int i = 2; i <= n; i++) scanf("%d", &pre[i]);
//初始的编号集合为 {1,2,3,4.....n}
for(int i = 1; i <= n; i++) r.push_back(i);
//倒序遍历
for(int i = n; i >= 1; i--){
//取出第pre[i]大的值(因为r是升序排列的,直接按下标把值取出来即可)
//因为r的下标是从0开始的,因此下标为k的数其实是第 k+1 大的
ans.push_back(r[pre[i]]);
//编号集合去掉pre[i]
r.erase(r.begin() + pre[i]);
}
//因为前面是倒序遍历每一头牛,因此输出前需要先反转一下
reverse(ans.begin(), ans.end());
for(int i = 0; i < ans.size(); i++)
printf("%d\n", ans[i]);
return 0;
}
线段树解法(维护区间内还有多少数没有被用过)
线段树维护每个区间内有多少个没被用过的数。每次取出第m大的数后,区间内数的个数 -1。
而找到第m大的数,类似于在一个有序序列中找第m大的数,其实本质思想是二分了。
因此,复杂度为O(n*logn),肯定是可以过的
线段树代码:
//线段树的结点,保存着区间左右端点和区间中值的个数
struct node{
int v, l, r;
}tr[N * 4];
//建树
void build(int u, int l, int r){
//初始时[l, r]区间中的数的个数为 r - l + 1
tr[u] = {r - l + 1, l, r};
if(l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
//找第x大的数
int query(int u, int x){
//既然搜到了这个区间,那么x必定在这个区间里面,区间值的个数自然要-1
tr[u].v--;
//如果搜到了叶子结点,那么必定是这个值了
if(tr[u].l == tr[u].r) return tr[u].r;
//左子区间的值的个数不足x个,那么就在右子区间找第 x - k大的数(k为左区间数的个数)
if(tr[u << 1].v < x) return query(u << 1 | 1, x - tr[u << 1].v);
//左区间的数的个数 >= x,那么就在左区间搜索即可
return query(u << 1, x);
}
int pre[N];
VI ans;
int main(){
int n;
scanf("%d", &n);
for(int i = 2; i <= n; i++) scanf("%d", &pre[i]);
//建树
build(1, 1, n);
//倒序遍历,求出每头牛的序号
for(int i = n; i >= 1; i--) ans.push_back(query(1, pre[i] + 1));
//翻转输出
reverse(ans.begin(), ans.end());
for(int i = 0; i < ans.size(); i++) printf("%d\n", ans[i]);
return 0;
}
树状数组解法
单点修改,区间查询也可以用树状数组的,代码还短一些!
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mst(a, b) memset((a), (b), sizeof (a))
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define prt(x) printf("%d\n", x)
#define prtl(x) printf("%lld\n", x)
#define debug(x) cout << #x << ": " << x << endl;
#define pY putlls("YES")
#define pN puts("NO")
typedef vector<int> VI;
typedef long long LL;
typedef pair<int, int> PII;
typedef priority_queue<int, vector<int>, greater<int> > PQ;
typedef priority_queue<int, vector<int>, less<int> > QP;
int n, m;
const int N = 200010, M = 510, INF = 0x3f3f3f3f;
int book[N], a[N];
int val[N], res[N];
inline int lowbit(int x) {return x & -x;}
void update(int x, int c){
while(x <= n){
val[x] += c;
x += lowbit(x);
}
}
int getsum(int x){
int sum = 0;
while(x){
sum += val[x];
x -= lowbit(x);
}
return sum;
}
//二分查找第k大的数,肯定是递增的哇
int findK(int k){
int l = 1, r = n;
while(l < r){
int mid = l + r >> 1;
int t = getsum(mid);
if(t < k) l = mid + 1;
else r = mid;
}
return l;
}
int main(){
int __ = 1;
// sc(__);
while(__--){
sc(n);
for(int i = 1; i <= n; i++){
if(i > 1) sc(a[i]);
update(i, 1);
}
for(int i = n; i >= 1; i--){
//找第a[i]+1大的数
int k = findK(a[i] + 1);
update(k, -1);
//用过了,就减去1
res[i] = k;
}
for(int i = 1; i <= n; i++) prt(res[i]);
}
return 0;
}
都看到这了,也不差👍的那点时间了吧~😘