poj2182 Lost Cows 线段树 二分+树状数组 模板

版权声明:点个关注(^-^)V https://blog.csdn.net/weixin_41793113/article/details/89193260

描述

N(2 <= N <= 8,000)母牛具有1..N范围内的独特品牌。在一个表现不佳的壮观表现中,他们参观了附近的“水坑”,并在晚餐前喝了几杯啤酒。当是时候排队吃晚餐时,他们没有按照品牌所需的数字顺序排列。 

遗憾的是,FJ没有办法对它们进行排序。此外,他不善于观察问题。他没有记下每头牛的品牌,而是确定了一个相当愚蠢的统计数据:对于每头排队的牛,他都知道排在那头牛之前的奶牛数量实际上比那头牛的品牌小。 

根据这些数据,告诉FJ奶牛的确切排序。 

输入

*第1行:单个整数,N 

*行2..N:这些N-1行描述了在给定的奶牛排行之前并且品牌小于该奶牛的奶牛数量。当然,没有奶牛排在第一头牛的前面,所以她没有列出。输入的第2行描述了品牌小于插槽#2中的奶牛的前面奶牛的数量; 第3行描述了品牌小于3号槽中奶牛的前奶牛数量; 等等。 

输出

*第1..N行:N行输出中的每一行都告诉品牌一头牛。输出的第1行告诉品牌第一头牛; 第2行告诉第二头牛的品牌; 等等。

样本输入

5
1
2
1
0

样本输出

2
4
5
3
1

资源

USACO 2003美国公开橙

解法1:暴力

 数据规模n≤8000,O(n^2)的复杂度可以AC,每次找出最小点,确定该点后,就可以逐步确定排位了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int a[10005];
int ans[10005];
int main(){
    int n,cnt;
    while(~scanf("%d",&n)){
        a[1] = 0;
        cnt = 1;
        memset(ans,0,sizeof(ans));
        for(int i=2;i<=n;i++)
            scanf("%d",&a[i]);
        for(int k=1;k<=n;k++)
            for(int i=n;i>=1;i--)
                if(a[i]==0){
                    ans[i] = cnt++;
                    for(int j=i;j<=n;j++)
                        a[j]--;//这点的排位确定了,对其后面的点产生了影响,因为该点是最小点,他后面的点都要进一位
                    a[i] = 9999999;//相等于删除该点的权值
                    break;
                }
        for(int i=1;i<=n;i++)
                printf("%d\n",ans[i]);
    }

	return 0;
}

解法2:线段树

每次也是从最后一个点开始确定该点的排位,因为该点的pre数组值是包括了整个数组,换句话说,就是1-n中排第几小是确定的,所以从它入手可以逐步确定答案,找出该点的编号后,还需要维护线段树的len值,即该区间还有多少个未确定的点

#include<iostream>
#include<cstdio>

using namespace std;

const int MAX = 10000+5;

struct {
    int l,r,len;//len记录有多少个点未被删的点
} tree[4*MAX];

int pre[MAX],ans[MAX];

void buildTree(int u,int l,int r){
    tree[u].l = l;
    tree[u].r = r;
    tree[u].len = r-l+1;
    if(l==r)
        return;
    int mid = l + (r-l)/2;
    buildTree(2*u,l,mid);
    buildTree(2*u+1,mid+1,r);
}

int query(int u,int num){
    tree[u].len--;
    if(tree[u].l == tree[u].r)
        return tree[u].l;
    if(tree[2*u].len>=num)//前面一半的点的数量已经可以够确定排位了就在左孩子找,否则在右孩子找num-左孩子总的点数
        return query(2*u,num);
    else
        return query(2*u+1,num-tree[2*u].len);
}

int main(){
    int n;
    while(~scanf("%d",&n)){
        pre[1] = 0;
        for(int i=2;i<=n;i++)
            scanf("%d",&pre[i]);
        buildTree(1,1,n);

        for(int i=n;i>=1;i--)
            ans[i] = query(1,pre[i]+1);

        for(int i=1;i<=n;i++)
            printf("%d\n",ans[i]);
    }

	return 0;
}

解法3:二分+树状数组

树状数组的模板 

lowbit(x) = x & -x

功能:找到x的二进制数的最后一个1

x

 1

 

2

3

4

5

6

7

8

 

9

x的二进制

1

10

11

100

101

110

111

1000

1001

lowbit(x)

1

2

1

4

1

2

1

8

1

#define lowbit(x)  ((x) & - (x))   
void add(int x, int d){  //ax = ax + d
	while(x <= n) {
	   tree[x] += d;  x += lowbit(x); 
	}
}
int sum(int x) {       //求和:sum=a1+...+ax
	int sum = 0;
	while(x > 0){
	   sum += tree[x];  x -= lowbit(x);
	}
	return sum;
}

基于tree[]的计算

1)求和 sum = a1 + ... + ax

   利用tree[]数组求sum,例如:

sum[8] = tree[8]

sum[7] = tree[7] + tree[6] + tree[4]

sum[9] = tree[9] + tree[8]

 

以上关系是如何得到的?借助lowbit(x)

例:sum[7] = tree[7] + tree[6] + tree[4]

 

17开始,加上tree[7]

27 - lowbit(7) = 6,加上tree[6]

36 - lowbit(6) = 4,加上tree[4]

44 - lowbit(4) = 0,结束。

 

tree[]的更新

 

更改ax,和它相关的tree都会变化。例如改变a3,那么tree[3]tree[4]tree[8]...都会改变。

会影响哪些tree[]?   仍然利用lowbit(x)

1更改tree[3]

23 + lowbit(3) = 4,更改tree[4]

34 + lowbit(4) = 8,更改tree[8]

4直到最后的tree[n]

#include<iostream>
#include<cstdio>
using namespace std;

#define lowbit(x) ((x) & -(x))

const int MAX = 100005;
int tree[MAX],pre[MAX],ans[MAX];
int n;

void add(int x,int d){
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}

int sum(int x){
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}

int findpos(int x){//套用二分答案的模板
    int l=1,r=n,ans=1;
    while(l<=r){
        int mid = l + (r-l)/2;
        if(sum(mid)>=x){
            r = mid-1;
            ans = mid;
        }else
            l = mid+1;
    }
    return ans;
}


int main(){

    while(~scanf("%d",&n)){
        pre[1] = 0;
        for(int i=2;i<=n;i++)
            scanf("%d",&pre[i]);

        for(int i=1;i<=n;i++)
            tree[i] = lowbit(i);//初始化tree数组

        for(int i=n;i>=1;i--){
            int x = findpos(pre[i]+1);
            ans[i] = x;
            add(x,-1);//维护树状数组
        }
        for(int i=1;i<=n;i++)
            printf("%d\n",ans[i]);
    }

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41793113/article/details/89193260