题目链接
Description
题意:
FJ有n头牛,现在它们排成一排,每一头牛都有一个不同的编号(1-n),现在知道从第二头牛开始每头牛左边比自己编号小的牛的数目,让你确定每头牛的编号。
思路:
我用一个树状数组来表示第i头牛的左边有多少比自己标号小的,树状数组必须初始化,每个节点必须加1。每次我们可以知道最后一头牛的编号x,然后然后更新x以后的区间使其都减1(这里也可以使用线段树,不过感觉比较麻烦,因为树状数组的特性,只需要更新x就好了),然后求倒数第二头的编号,我们只需要找到一个x,使得sum(x) 为该牛左边小于其编号牛的数量,使用二分查找可以提高查找速率,然后是倒数第三头的………………………………
因为是倒着来的,为了熟悉stl,我使用了其封装的stack 栈。
下面是解题代码:
#include <stdio.h> #include <string.h> #include <stack> #define maxn 8005 using namespace std; int a[maxn]; int ans[maxn]; int n; int lowbit(int x) { return x&(-x); } void add(int x, int v) { while (x <= n) { a[x] += v; x += lowbit(x); } } int sum(int x) { int s = 0; while (x > 0) { s += a[x]; x -= lowbit(x); } return s; } int binarysearch(int l, int r, int x) { if (l == r) return l; int mid = (l + r) >> 1; if (x <= sum(mid)) return binarysearch(l, mid, x); else return binarysearch(mid+1, r, x); } /*二分查找也可以写成非递归的形式,避免了函数的调用和栈的开销,节约时间空间, 但递归的形式容易理解,也不易出错,各有优劣,这就看你如何取舍了*/ stack <int> s; int main() { int t; while (scanf("%d",&n) != EOF) { memset(a, 0, sizeof(a)); //因为每次使用s后s必定为空,就不用初始化了 int i; for (i = 1; i < n; i++) { add(i+1,1); scanf("%d",&t); s.push(t); } while (i > 1) { int x = binarysearch(1, n, s.top()); ans[i--] = x; add(x, -1); s.pop(); } ans[1] = binarysearch(1, n, 0); //while循环只循环n-1次(如果循环n次栈会出错),所以要加这行 for (i = 1; i <= n; i++) { printf("%d\n",ans[i]); } } return 0; }