【2019河北省程序设计竞赛】E-Paper Plane Fly Away

2019河北省程序设计竞赛  

本题运用树状数组求逆序对数的方法,如果不了解树状数组,可以先去学一下。

求每个男生扔出飞机后可能与之相撞的飞机数,分为两部分求:

1、男生x左边的每个男生i   如果男生i喜欢的女生所在位置大于男生x喜欢女生的所在位置,即pos[like[i]]>pos[like[x]],则可能相撞,计数+1;(左边与之构成的逆序对数)

2、男生x右边的每个男生i,如果男生i喜欢的女生所在位置小于男生x喜欢女生的所在位置,即pos[like[i]]<pos[like[x]],则可能相撞,计数+1;(右边与之构成的逆序对数)

代码部分处理了like数组,用每个女生的座位号来代替女生的id号,即like[i]=pos[like[i]],后面讲解我们直接用like[i]表示男生i喜欢女生的所在位置编号。

以题中测试样例为例

每个男生i喜欢的女生位置pos[i]分别是:4 2 5 1 3
cns[1]=0+3;
cns[2]=1+1;
cns[3]=0+2;
cns[4]=3+0;
cns[5]=2+0;

后面我们只用到pos,pos[i]为每个男生i喜欢的女生位置。

1、求左边pos[i]比pos[x]大的个数,cnsleft=i-1-sum(pos[x]);   

解释:i-1为[1,n]区间的数出现了几个,sum(pos[x])为为[1,pos[x]]区间的数出现了几个,那么i-1-sum(x)为[pos[x]+1,n]区间的数出现了几个。

2、同样,求右边pos[i]比pos[x]小的数,cnsright=sum(pos[x]);(将add(pos[x]放在统计代码后,不然会将自身算进去,如果用sum(pos[x]-1)则无须考虑两句代码的先后位置)


在这里我的处理时,先求右半部分,统计右边,故从n到1遍历

重置树状数组,再求左半部分,统计左边,故从1到n遍历

直接在第二次循环中输出cns即可。(注:如果先求左,再求右,也可,只是要多写一个for循环去输出了)

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N=100005;
int n;
int t[N];//树状数组,t[x]=1,表示x已出现
int pos[N];//女孩位置
int like[N];//心意人
int cns[N];//计数
int lowbit(int x){
    return x&-x;
}
void add(int x){//单点修改
    for(int i=x;i<=n;i+=lowbit(i)){
        t[i]+=1;
    }
}
int sum(int x){//求和,sum(x)表示[1,x]区间中的数出现了几个
    int ans=0;
    while(x){
        ans+=t[x];
        x-=lowbit(x);
    }
    return ans;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        int fro;
        cin>>fro>>like[i];
        like[i]-=n;//女孩标号为n+1~2n,且无重复,可以存储她们标号-n的值,节省空间
        pos[fro-n]=i;//女孩标号-n,记录每个女孩的位置
    }
    for(int i=n;i>=1;i--){
        like[i]=pos[like[i]];//用每个女生的座位号来代替女生的id号
        cns[i]+=sum(like[i]);//统计右边比like[i]小的个数
        add(like[i]);//将like[i]加入到树状数组,本行与上一行位置不可调换
    }
    memset(t,0,sizeof t);//重置树状数组
    for(int i=1;i<=n;i++){
        cns[i]+=sum(n)-sum(like[i]);//统计左边比like[i]大的个数
        add(like[i]);//将like[i]加入到树状数组
        cout<<cns[i]<<endl;
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值