• poj2182--Lost Cows--树状数组+二分


    Description

    现在有n头牛,它们的编号1~n现在的排列是乱序的。只给出每一头牛的前面比它编号小的数量low,求确定每头牛的正确编号。

    Sample Input

    5

    1

    2

    1

    0

    Sample Output

    2

    4

    5

    3

    1

    题解:

      这题我写了暴力……交上去跑得很慢还居然过了,qwq但是课件里说这题是树状数组,那好吧再写一遍……

      首先我们推一波样例,总共有5头牛,从第二头开始小于当前牛的数量依次是1,2,1,0。最后一头牛的地方是0,就是说所有的牛都比它高,那么这头牛编号一定是1。不难看出,最后一个位置的牛的low,决定了这头牛的编号为low+1。

      我们可以根据这个规律来倒着找,从想法上来讲,是每次确定了一个牛以后,就在所有编号里删去这头牛的编号。这样每次都可以从最后一个位置找。

      而又有一个事实:当前位置的牛,它的编号=前面小于它的数量+后面小于它的数量+1(我是从这里写的暴力)

      我们可以用树状数组维护当前牛的前面比它小的数量。树状数组中的数组取0或者1,表示编号有没有被删。

      前面小于它的数量=它的编号-后面小于它的数量-1

      移项一下就不难看出来单调性了……(这里等式左边越大的话,表明等式右边的两个被减数越小,且减数越大)

      所以我们可以开心地二分了。

      当然用线段树呢是可以省一个log的复杂度的,因为线段树直接可以通过转移到左子树或右子树来实现二分。

      (copy了老干部的板子)

     1 #include<cstdio>
     2 #include<algorithm>
     3 using namespace std;
     4 const int maxn=10000;
     5 int low[maxn];
     6 int h[maxn];
     7 int f[maxn];
     8 int n;
     9 int lowbit(int x)
    10 {
    11     return(x&-x);
    12 }
    13 int get(int x)//查询前x项的和(有多少比i小)
    14 {
    15     int ret=0;
    16     while(x)
    17     {
    18         ret+=f[x];//f[i]==0----当前数字被删,f[i]==1----当前数字可选
    19         x-=lowbit(x);
    20     }
    21     return ret;
    22 }
    23 
    24 void add(int p,int c)
    25 {
    26     while(p<=n)
    27     {
    28         f[p]+=c;
    29         p+=lowbit(p);
    30     }
    31     return;
    32 }
    33 
    34 int fin(int x)
    35 {
    36     int l=1,r=n,mid,s;
    37     while(l<r)
    38     {
    39         mid=(l+r)>>1;
    40         s=get(mid);
    41         if(s>x) r=mid-1;
    42         else if(s<x)
    43             l=mid+1;
    44         else
    45         {
    46             if(r==mid)
    47             {
    48                 if(get(l)==x)
    49                     return l;
    50                 return r;
    51             }
    52             r=mid;
    53         }
    54     }
    55     return l;
    56 }
    57 
    58 int main()
    59 {
    60     scanf("%d",&n);
    61     add(1,1);
    62     for(int i=2;i<=n;i++)
    63     {
    64         scanf("%d",&low[i]);
    65         add(i,1);
    66     }
    67     for(int i=n;i;i--)
    68     {
    69         int k=fin(low[i]+1);
    70         h[i]=k;
    71         add(k,-1);
    72     }
    73     for(int i=1;i<=n;i++)
    74         if(f[i])
    75         {
    76             h[1]=f[i];
    77             break;
    78         }
    79     for(int i=1;i<=n;i++)
    80         printf("%d
    ",h[i]);
    81     return 0;
    82 }
    View Code
  • 相关阅读:
    bzoj1934 Vote 善意的投票 最小割(最大匹配)
    poj3417 Network 树上差分+LCA
    bzoj1076 奖励关 期望dp
    bzoj1087 互不侵犯King 状压dp+bitset
    bzoj1041 圆上的整点 数学
    bzoj 1085骑士精神 迭代深搜
    CodeForces 1043D Mysterious Crime 区间合并
    2018.12.14 浪在ACM 集训队第九次测试赛
    2018.12.9 中国石油大学第四次新生训练赛题解
    2018.12.8 中国石油大学第三次新生训练赛题解
  • 原文地址:https://www.cnblogs.com/Beckinsale/p/7475125.html
Copyright © 2020-2023  润新知