CF 350D 直线映射 + 差分

刚开始想的按圆半径排序, 按线段的x坐标较小的那个点排序, 二分缩小范围,
第12个case T了, 才意识到不是纯粹几何题.


后来想到了斜率 + 截距映射直线, 然后要考虑映射是同一条直线的线段集合在以为空间上映射的点的次数, 其中还有斜率不存在情况, 只能想到树状数组, 没接触过差分.
然后这个这个映射 + 次数的预处理要用到不止一种stl, 觉得自己能力不足只能去看题解:
1. 预处理线段映射到直线
2. 枚举圆对, 得出要判断中点的在映射直线(过圆心连线的中点且与其垂直的直线)上的点(斜率存在是x坐标, 不存在取y坐标)
3. 在2中映射直线中二分查找点的位置, 根据预处理的ans获取该直线上的解

code:

#include <iostream>
#include <utility>
#include <vector>
#include <map>
#include <algorithm>
#include <cstdio>
using namespace std;

const int N = 300010;

typedef pair<pair<int, int>, int> Line;/**两点映射的直线(向量) + 截距*/
//typedef pair<Line, pair<int, int> > Seg;
map<Line, vector<int> >ans;/**存线段在一维空间上映射点的次数*/
typedef map<Line, vector< pair<int, int> > > ll;/**映射*/
ll l;
ll::iterator it;
int n, m;

struct Point{
    int x, y;
    Point(int _x = 0, int _y = 0): x(_x), y(_y){};
    Point operator - (const Point &rhs){
        return Point(x - rhs.x, y - rhs.y);
    }
    Point operator * (int v){
        return Point(v * x, v * y);
    }
    Point operator + (const Point &rhs){
        return Point(x + rhs.x, y + rhs.y);
    }
    Point operator / (int v){
        return Point(x / v, y / v);
    }
    int len(){
        return x * x + y * y;
    }
    friend int dis(Point a, Point b){
        return Point(a - b).len();
    }
    void read(){
        scanf("%d%d", &x, &y);
    }
};

struct Circle{
    int x, y, r;
}c[1510];

inline int Pow(int x) {return x * x;}
inline int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);}

Line getLine(Point a, Point b){/**预处理直线*/
    if(a.x == b.x) return make_pair(make_pair(0, 0), a.x);/**不存在斜率*/
    if(a.y == b.y) return make_pair(make_pair(1, 0), a.y);/**斜率为0*/
    int dx = a.x - b.x;
    int dy = a.y - b.y;
    int d = gcd(abs(dx), abs(dy));
    dx /= d;
    dy /= d;
    if(dx < 0){
        dx *= -1;
        dy *= -1;
    }
    int v = (a.y % dy + dy ) % dy;
    v = a.x + (v - a.y) / dy * dx;
    return make_pair(make_pair(dx, dy), v);/**同一条直线映射唯一*/
}

int getAns(Line tmp, int x){
    if(l.find(tmp) == l.end()) return 0;
    vector<pair<int, int> > & u = l[tmp];
    int p = upper_bound(u.begin(), u.end(), make_pair(x, 1)) - u.begin();/**二分*/
    return ans[tmp][p];
}

Line getMirrorLine(Point a, Point b){/**两圆心中点的垂线*/
    Point mid = (a + b) / 2,off = a - b;
    swap(off.x,off.y), off.x *= -1;
    return getLine(mid + off,mid - off);
}

int main(){
    //freopen("in.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    Point a, b;
    while(n--){
        a.read();
        b.read();
        a = a * 2;/** *2 规避浮点误差 */
        b = b * 2;
        if(a.x == b.x) {
            l[getLine(a, b)].push_back(make_pair(min(a.y, b.y), 1));
            l[getLine(a, b)].push_back(make_pair(max(a.y, b.y) + 1, -1));
        }
        else {
            l[getLine(a, b)].push_back(make_pair(min(a.x, b.x), 1));
            l[getLine(a, b)].push_back(make_pair(max(a.x, b.x) + 1, -1));
        }
    }
    for(it = l.begin(); it != l.end(); ++it){/**枚举直线*/
        sort((*it).second.begin(), (*it).second.end());/**端点排序*/
        vector<int> & tt = ans[(*it).first];
        vector<pair<int, int> > & d = (*it).second;
        int v = 0;
        tt.push_back(v);
        for(int i = 0; i <= d.size(); ++i){/**预处理ans*/
            v += d[i].second;
            tt.push_back(v);
        }

    }
    for(int i = 1; i <= m; ++i){
        scanf("%d%d%d", &c[i].x, &c[i].y, &c[i].r);
        c[i].x *= 2;
        c[i].y *= 2;
        c[i].r *= 2;
    }
    int cnt = 0;
    for(int i = 1; i < m; ++i)
        for(int j = i + 1; j <= m; ++j){
            if(c[i].r == c[j].r && dis(Point(c[i].x, c[i].y), Point(c[j].x, c[j].y)) > Pow(c[i].r + c[j].r)){
                int mid;
                if(c[i].y == c[j].y) mid = c[i].y;/**特判无斜率情况*/
                else mid = (c[i].x + c[j].x) >> 1;
                cnt += getAns(getMirrorLine(Point(c[i].x, c[i].y), Point(c[j].x, c[j].y)), mid);
            }
        }
    printf("%d\n", cnt);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值