POJ 2182 Lost Cows

        有一组乱序的数列,数列有N个,范围是1-N,相同数字不重复。现给定另一组数列,新数列的第k位的值表示在原数列的第k位之前有几个数比其小。求原数列。

        用线段树或树状数组+二分可以做,因为这个问题可以转化为求第k大数。

        从新数列的最后一位a[n]开始看,因为在之前有a[n]个数比其大,原数列的值一定是a[n]+1。确定一个元素的位置后,从1~N这个序列中删除a[n]+1 那么到了a[n-1],在删除了a[n]+1后,需要确定a[n-1]+1是第几大的数。这样一直求完,最后倒序输出就可以了。

        线段树经典题的题解:http://blog.csdn.net/sssogs/article/category/1235231

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct T
{
    int l;
    int r;
    int mid;
    int v;
};
T tr[20000];
void build(int l,int r,int c)
{
    tr[c].l=l;
    tr[c].r=r;
    tr[c].mid=(l+r)>>1;
    tr[c].v=r-l+1;
    if (l == r)
        return ;
    build(l,tr[c].mid,c<<1);
    build(tr[c].mid+1,r,c<<1|1);
}
void change(int c,int n)
{
    tr[c].v--;
    if (tr[c].l == n && tr[c].r == n)
        return ;
    if (tr[c].mid >= n)
        change(c<<1,n);
    else
        change(c<<1|1,n);
}
int query(int c,int n)
{
    if (tr[c].l == tr[c].r)
        return tr[c].l;
    if (tr[c<<1].v > n)
    {
        query(c<<1,n);
    }
    else
    {
        n-=tr[c<<1].v;
        query(c<<1|1,n);
    }
}
int main()
{
    int n,a[8010],i;
    scanf("%d",&n);
    a[0]=0;
    for (i=1; i<n; i++)
    {
        scanf("%d",a+i);
    }
    build(1,n,1);
    for (i=n-1; i>=0; i--)
    {
        a[i]=query(1,a[i]);
        change(1,a[i]);
    }
    for (i=0; i<n; i++)
    {
        printf("%d\n",a[i]);
    }
}


你可能感兴趣的:(POJ 2182 Lost Cows)