zoukankan      html  css  js  c++  java
  • [Usaco2003 Open]Lost Cows

    [Usaco2003 Open]Lost Cows

    描述

    给你一个数字N,2 <= N <= 8,000 
    再给出一个N的全排列,乱序排列 
    告诉你从第2个位置到第N个位置,每个位置的前面的数字中比它小的数的个数 
    求每个位置的数字是多少  

    输入

    第一行给出数字N 
    接下来N-1行,每行给出一个数字 

    输出

    有N行 
    每行一个数字,代表这个位置上的数字是多少  

    样例

    输入

    5 
    1 
    2 
    1 
    0 

    输出

    2 
    4 
    5 
    3 
    1 
    思路:
    看看题面,一看就是倒循环来搞,再看看n,小于等于8000,我一惊,这道题不是暴力就可以过吗,于是5分钟敲了一个暴力代码,具体思
    路见代码:
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int a[10001];//记录比它小的数字个数 
     4 int f[10001];//逻辑数组
     5 int ans[10001];//最终计算的结果
     6 int main()
     7 {
     8     int n;
     9     scanf("%d",&n);
    10     for(int i=2;i<=n;i++)
    11         scanf("%d",&a[i]);
    12     for(int i=1;i<=n;i++)
    13         f[i]=1;//初始值全部为1 
    14     ans[n]=a[n]+1;//最后一个数字显然是a[n]+1(前面有a[n]个比它小的) 
    15     f[ans[n]]=0;//这个数字用过了,打上标记 
    16     for(int i=n-1;i>=1;i--)//从后往前推 
    17     {
    18         int num=0;//用num记录前面有多少个比它小的 
    19         for(int j=1;i<=n;j++)//枚举每一个数(vis数组) 
    20         {
    21             if(f[j]==1)//如果这个数没有被用过 
    22             {
    23                 num++;//统计比它小的数字个数
    24                 if(num==a[i]+1) //找到了这个数 
    25                 {
    26                     f[j]=0;//打上标记 
    27                     ans[i]=j;//赋值 
    28                     break;//因为我们是要找最小的符合条件的,因此要break
    29                     //否则有可能会在后面再找到符合条件的,把它更新了。。。 
    30                 }
    31             }
    32         }
    33     }
    34     for(int i=1;i<=n;i++)
    35         printf("%d
    ",ans[i]);
    36     return 0; 
    37 }

    我们再来看看这个代码,注意到第21,23行,这是不是就是统计在a[i]+1这个数前面有多少个f[j]为1?也就是一个求前缀和的过程,既然是求前缀和,那我们是不是可以

    用树状数组来进行优化?还是和上面那个f[]数组一样,我们首先在每个位置上插入一个1,找到答案了就在这个位置上插上-1(该位置变成0),然后现在此问题就变成

    了让我们用树状数组来维护一个01串了,我们要找的答案其实就是位置<=a[i]+1的数有多少个是1。。。。

    我们来模拟一下样例1 2 1 0

    首先预处理 1 1 1 1 1 ,

    然后我们倒循环找ask(0+1)=ask(1)=1,所以ans[n]=1,同时把第一个位置变成0,原序列变成 0 1 1 1 1

    接着找ask(1+1)=ask(2)=3,所以ans[4]=3,和上面一样,原序列变成 0 1 0 1 1

    再找ask(2+1)=ask(3)=5,所以ans[3]=5,原序列变成 0 1 0 1 0

    我们再找ask(1+1)=ask(2),但是我们注意到上面这个序列,前面四个数的前缀和为2,前面五个数的前缀和也为2!!所以这个时候我们就要贪心一点,尽量选

    靠左边的数,于是ans[2]=4,原序列变成 0 1 0 0 0,

    最后找ask(0+1)=1,ans=[1]=2,原序列变成0 0 0 0 0,

    于是最后的答案便是 2 4 5 3 1。。。。具体细节见代码:

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 ll n,ans[80001],sum[500010],a[500010];
     5 ll lowbit(ll n) {
     6     return n&(-n);
     7 }
     8 void add(ll x,ll val) {
     9     while(x<=n) {
    10         sum[x]+=val;
    11         x+=lowbit(x);
    12     }
    13 }
    14 ll ask(ll x) {
    15     ll t=0;
    16     while(x) {
    17         t+=sum[x];
    18         x-=lowbit(x);
    19     }
    20     return t;
    21 }
    22 ll serch(ll x)
    23 {
    24     ll l=1,r=n,loc=n;//loc代表答案 
    25     while(l<=r)
    26     {
    27         ll mid=(l+r)>>1;
    28         if(ask(mid)==x)//如果找到了符合条件的数 
    29         {
    30             if(mid<loc)//如果mid在loc左边(比loc小) 
    31                 loc=mid;//我们要找最靠左的答案
    32                 //因此如果发现了比它小的就更新 
    33             r=mid-1;//同时把右端点更新,尝试去找更小的数 
    34         }
    35         else if(ask(mid)>x)//如果前缀和大于x,说明答案在mid左边 
    36         r=mid-1;//右端点移一下 
    37         else
    38         l=mid+1;//否则就在左边,移一下 
    39     }
    40     return loc;
    41 }
    42 int main() {
    43     scanf("%lld",&n);
    44     for(ll i=2; i<=n; i++) 
    45         scanf("%lld",&a[i]);
    46     for(ll i=1;i<=n;i++)
    47     add(i,1);//预处理,每个位置插上1 
    48     for(ll i=n; i>=1; i--) {//从后往前推 
    49         ll k=serch(a[i]+1);//二分找答案 
    50         add(k,-1);//把这个位置打上标记,用过了 
    51         ans[i]=k;//储存起来 
    52     }
    53     for(ll i=1;i<=n;i++)
    54     printf("%lld
    ",ans[i]);
    55     return 0;
    56 }

    于是,我们就快乐地AC啦~~~

    如果有哪些地方写的不对的话欢迎各位巨佬指正,如果您觉得我写的对您的学习有帮助的话,请给博主一个小小的赞,您的支持将是我前进的动力~~~

  • 相关阅读:
    js函数——倒计时模块+无缝滚动
    一步步编写avalon组件02:分页组件
    mvc5+ef6+Bootstrap 项目心得--身份验证和权限管理
    只用css实现“每列四行,加载完一列后数据自动填充到下一列”的效果
    某考试 T1 arg
    vijos 2035 奇数偶数与绚丽多彩的数
    bzoj 5093: [Lydsy1711月赛]图的价值
    [HEOI2016/TJOI2016]求和
    [TJOI2015]概率论
    Codeforces 616 E Sum of Remainders
  • 原文地址:https://www.cnblogs.com/sbwll/p/13197480.html
Copyright © 2011-2022 走看看