2016 UESTC Training for Data Structures E - 卿学姐与城堡的墙 树状数组求逆序对、离散化

117 篇文章 2 订阅
13 篇文章 0 订阅

E - 卿学姐与城堡的墙

Time Limit: 2000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
 

卿学姐终于来到了魔王的城堡,城堡修建的十分壮观。

即使心中放不下公主,卿学姐还是忍不住驻足观赏这宏伟的建筑。

卿学姐注意到城堡的墙上有若干直线状的花纹。

可以将墙看做一个平面,卿学姐想知道有多少种方式任取两个直线,使得这两个直线的交点的横坐标 x x满足: uxv u≤x≤v

Input

第一行三个整数 N,u,v  标明直线有 N 条。

接下来有 N N行,每行两个整数 k,b     表示这条直线是 y=kx+b

1N200000                                

0|k|1000000000                                  

0|b|1000000000                                     

0|u|1000000000                                  

0|v|1000000000                              

输入保证 uv  ,保证没有两条直线是一样的

Output

输出一个整数,代表选择的方法数。

Sample input and output

Sample Input Sample Output
3 -3 1
-1 3
2 2
1 1
3

Hint

title

上图是样例的解释,交点是A,B,C

Source

2016 UESTC Training for Data Structures  Problem E

My Solution

树状数组求逆序数

先对uy进行排序,如果a.uy != b.uy 那么uy大的在上面;

    如果a.uy == b.uy 那么看vy   vy小的在上面, 大的在下面

然后进行1 ~ N的离散化操作,(用树状数组了所以数组不能太大的)

这样就相当于把左边u上的点移到uv内部的,虽然有交点,根据vy大小,有的往上拉,有的往下拉;


然后对对右边vy进行排序,如果a.vy != b.vy 那么uy大的在上面;

          如果a.vy == b.vy 那么看uy   uy小的在上面, 大的在下面

这样排序相当于把右边v上的交点也移到uv内部去了;

所以已经处理成交点只在uv内部了

然后for i = 1 ~ N

add(seg[i-1].order, 1);

ans += i - get(seg[i-1].order);

扫完就可以输出了☺☺

复杂度 O(nlogn)

另外这里附上一个比较好的讲树状数组求逆序数原理的博客

http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;
const int maxn = 200000 + 8;

map<long long,int> s;      //用来处理 u == v 的情况
int N;               //具体操作的时候要用所以放到全局
struct segment{
    long long vy, uy;
    int order;
} seg[maxn];

bool cmpu(const segment& a, const segment& b)
{
    if(a.uy != b.uy) return a.uy > b.uy;
    else return a.vy < b.vy;
}

bool cmpv(const segment& a, const segment& b)
{
    if(a.vy != b.vy) return a.vy > b.vy;
    else return a.uy < b.uy;
}

//!树状数组求逆序对
int Tree[maxn];

inline int lowbit(int x)
{
    return (x&-x);
}

void add(int x, int value)
{
    for(int i = x; i <= N; i += lowbit(i))
        Tree[i] += value;
}

int get(int x)
{
    int sum = 0;
    for(int i = x; i; i -= lowbit(i))
        sum += Tree[i];
    return sum;
}

//!!!!!! u, v, k, b; 会在seg[i].uy = k*u + b; seg[i].vy = k*v + b;这中间过程溢出,所以也要 long long 才行了。
//!!!!!! 这里改了然后过来,WA了10次test1,......test就玩溢出,坑坏我了,前天被坑起啊(┬_┬)
int main()
{
    #ifdef LOCAL
    freopen("a.txt", "r", stdin);
    #endif // LOCAL
    long long u, v, k, b;
    //!u <= v   ...... u == v
    long long ans = 0, crosspointsinu = 0;
    scanf("%d%lld%lld", &N, &u, &v);
    //!....... u == v 的情况处理错了

    for(int i = 0; i < N; i++){
        scanf("%lld%lld", &k, &b);
        seg[i].uy = k*u + b;
        seg[i].vy = k*v + b;
        s[seg[i].uy]++;
    }
    //据说可以用set去重
    sort(seg, seg + N, cmpu);
    for(int i = 0; i < N; i++){
        seg[i].order = i+1;   //根据u的值进行离散化
    }
    //

    if(u == v){
        int times;
        for(auto i = s.begin(); i != s.end(); i++){
            times = i->second;
            if(times != 1){
                crosspointsinu += (times*(times-1))/2;
            }
        }
        printf("%lld", crosspointsinu);
    }
    else{
        sort(seg, seg + N, cmpv);

        memset(Tree, 0, sizeof Tree);
        for(int i = 1; i <= N; i++){
            add(seg[i-1].order, 1);
            ans += i - get(seg[i-1].order);

        }
        printf("%lld", ans);
    }
    return 0;
}

Thank you
                                                                                                                                               ------from  ProLights

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值