斜率小于0的连线数量 51NOD - 1107

点击打开链接

题目要求再一共 n*(n-1)/2条连线中找出所有斜率小于零的

其实就是累加每个点右下方有多少个点 或者累加每个点左上方有多少个点 这样就转换成了求逆序对的问题

注意处理一下 斜率为零或不存在的情况就好

 

这里抛开题目 只谈求一个排列的逆序对

在线性代数中 我们有三种方法求一个排列的逆序对

1 从左到右 看每一个元素左边有多少比它大的

2 从右到左 看每一个元素右边有多少比它小的

3 从小到大 看每个数的左边有多少比它大的 右边有多少比它小的 然后划去这个数 即划去法

我们采用第一种方法

对于一个排列 我们用a[n]数组保存 再用b[n]数组保存升序排序后的a[n] 然后基于b[n]建立线段树(值初始化为0)

遍历a[n] 对于每一个元素a[i] 先找到其在b[n]中的位置 p (如下代码所示建映射表 用map比较慢) 查询 [p n] 区间内已经有多少个数是之前已经遍历过的 累加这个值 然后更新p位置已来过即可

这样做的意义就是 看a[1]到a[i-1]中有多少个元素比a[i]要大

 

#include <bits/stdc++.h>
using namespace std;
#define ll long long

struct node1
{
    int x;
    int y;
    int id;
};

struct node2
{
    int l;
    int r;
    ll val;
};

node1 point[50010];
node2 tree[200010];
ll num[50010];
int pos[50010];
int n;

int cmp1(node1 n1,node1 n2);
int cmp2(node1 n1,node1 n2);
void build(int l,int r,int cur);
ll query(int pl,int pr,int cur);
void update(int tar,int cur);
void pushup(int cur);

int main()
{
    ll sum;
    int i,j,cnt;
    num[0]=0;
    for(i=1;i<=50000;i++)
    {
        num[i]=num[i-1]+i;
    }
    while(scanf("%d",&n)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&point[i].x,&point[i].y);
        }
        point[0].x=-1,point[0].y=-1;
        sum=0,cnt=0;
        sort(point+1,point+n+1,cmp1);
        for(i=1;i<=n;i++)
        {
            if(point[i].x==point[i-1].x)
            {
                cnt++;
            }
            else
            {
                sum-=num[cnt];
                cnt=0;
            }
            point[i].id=i;
        }
        sum-=num[cnt];
        cnt=0;
        sort(point+1,point+n+1,cmp2);
        for(i=1;i<=n;i++)
        {
            if(point[i].y==point[i-1].y)
            {
                cnt++;
            }
            else
            {
                sum-=num[cnt];
                cnt=0;
            }
            pos[point[i].id]=i;
        }
        sum-=num[cnt];
        build(1,n,1);
        for(i=1;i<=n;i++)
        {
            sum+=query(pos[i],n,1);
            update(pos[i],1);
        }
        printf("%lld\n",sum);
    }
    return 0;
}

int cmp1(node1 n1,node1 n2)
{
    if(n1.x==n2.x)
    {
        return n1.y>n2.y;
    }
    else
    {
        return n1.x<n2.x;
    }
}

int cmp2(node1 n1,node1 n2)
{
    if(n1.y==n2.y)
    {
        return n1.x>n2.x;
    }
    else
    {
        return n1.y<n2.y;
    }
}

void build(int l,int r,int cur)
{
    int m;
    tree[cur].l=l;
    tree[cur].r=r;
    tree[cur].val=0;
    if(l==r) return;
    m=(l+r)/2;
    build(l,m,cur*2);
    build(m+1,r,cur*2+1);
    return;
}

ll query(int pl,int pr,int cur)
{
    ll ans;
    if(pl<=tree[cur].l&&tree[cur].r<=pr)
    {
        return tree[cur].val;
    }
    if(tree[cur].l==tree[cur].r)
    {
        return 0;
    }
    ans=0;
    if(pl<=tree[cur*2].r) ans+=query(pl,pr,cur*2);
    if(pr>=tree[cur*2+1].l) ans+=query(pl,pr,cur*2+1);
    return ans;
}

void update(int tar,int cur)
{
    if(tree[cur].l==tree[cur].r)
    {
        tree[cur].val=1;
        return;
    }
    if(tar<=tree[cur*2].r) update(tar,cur*2);
    else update(tar,cur*2+1);
    pushup(cur);
    return;
}

void pushup(int cur)
{
    tree[cur].val=tree[cur*2].val+tree[cur*2+1].val;
    return;
}

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度校验整改及剩余电流监测试点应用站用交流系统断

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值