两道题一个套路:本质都是 gcd 约分后分类,直接把几何题变成 Map 计数题

二维:小苯的星轨

题目描述

平面直角坐标系中有 nnn 颗星星,第 iii 颗星星位于整点(xi,yi)。(x_i,y_i)。(xi​,yi​)。

保证所有星星的位置两两不同,但允许某颗星星位于原点 (0,0)(0,0)(0,0)。

小苯会选择两颗不同的星星,组成一个无序星星对。如果经过这两颗星星的直线同时经过原点 (0,0)(0,0)(0,0),那么称这个星星对是合法的。

注意:

1、 选择第 iii 颗和第 jjj 颗星星,与选择第 jjj 颗和第 iii 颗星星是同一个星星对;
2、 如果两颗星星中有一颗位于原点,那么这个星星对一定合法;
3、 由于所有星星的位置两两不同,任意两颗星星都能确定唯一一条直线。

请计算合法无序星星对的数量。

输入描述:

第一行包含一个整数 TTT,表示测试数据的组数。
对于每组测试数据:
第一行包含一个整数 nnn,表示星星的数量。

接下来 nnn 行,每行包含两个整数 xi,yix_i,y_ixi​,yi​,表示第 iii 颗星星的坐标。

数据范围如下:

1≤T≤100,1\le T\le100,1≤T≤100,
1≤n≤2×105,1\le n\le2\times10^5,1≤n≤2×105,
−109≤xi,yi≤109,-10^9\le x_i,y_i\le10^9,−109≤xi​,yi​≤109,
(xi,yi)≠(xj,yj)(i≠j)。(x_i,y_i)\ne(x_j,y_j)\quad(i\ne j)。(xi​,yi​)​=(xj​,yj​)(i​=j)。
保证所有测试数据中 nnn 的总和不超过 2×1052\times10^52×105。

输出描述:

对于每组测试数据,输出一行一个整数,表示合法无序星星对的数量。

示例1

输入

2
4
0 0
1 1
2 2
1 0
3
0 0
1 2
2 1

输出

4
2

说明

第一组数据中,合法的星星对为:

  • (0,0) 与 (1,1);

  • (0,0) 与 (2,2);

  • (0,0) 与 (1,0);

  • (1,1) 与 (2,2)。

因此答案为 4。

第二组数据中,只有位于原点的星星分别与另外两颗星星组成的两个星星对合法,因此答案为 2。

#include <iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<map>
#define int long long
using namespace std;
int gcd(int x, int y)
{
    return y == 0 ? x : gcd(y, x % y);
}
signed main()
{
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        vector<int>x(n);
        vector<int>y(n);
        for (int i = 0;i < n;i++)
            cin >> x[i] >> y[i];
        map<pair<int, int>, int>mp;
        for (int i = 0;i < n;i++)
        {
            if (x[i] == 0 && y[i] == 0)
            {
                mp[{0, 0}]++;
            }
            else if (x[i] == 0)
            {
                mp[{0, 1}]++;
            }
            else if (y[i] == 0)
            {
                mp[{1, 0}]++;
            }
            else
            {
                int g = gcd(x[i], y[i]);
                int xx = x[i] / g;
                int yy = y[i] / g;
                if (xx < 0)
                {
                    xx *= (-1);
                    yy *= (-1);
                }
                mp[{xx, yy}]++;
            }
        }
        int cnt = 0;
        for (auto e : mp)
        {
            cnt += (e.second - 1) * e.second / 2;
        }
        cnt += (n - mp[{0, 0}])* mp[{0, 0}];
        cout << cnt << "\n";
    }
    return 0;
}

三维:八角玄冰草·万向刺

题目描述

八角玄冰草作为霍雨浩的第四个魂灵,赋予了强大的冰属性能力。在史莱克学院的战术训练中,他正面对一个三维立体的虚拟训练场,场内散布着若干定位靶,他需要利用八角玄冰草赋予他的魂技——八角万向刺,以尽可能少的技能释放次数,将场上的所有定位靶悉数击破。
开始前,训练场会向霍雨浩推送一张战场地图:整个立体空间被均匀地划分为一个个格点,霍雨浩自己、每个定位靶、以及空位分别落在其对应的格点上。

这张地图可以看作一个 n × m × l 的三维立体字符矩阵,其中:
H 表示霍雨浩所在的位置,有且仅有一个
T 表示定位靶的位置
. 表示空位
霍雨浩全程站定于 H 所在的格点,不会移动。在此基础上,他每次可以朝三维空间中的任意一个方向(方向取值连续,不限于坐标轴或格点方向)释放万向刺,沿该方向从 H 出发形成一条无限长的射线;万向刺能穿透路径上的一切障碍,射线上所有的 T 都会被击破。已知定位靶不会移动,且所有目标都位于格点上。
两个定位靶能被同一次攻击击破,当且仅当它们与霍雨浩的连线方向完全相同(即方向向量成正实数倍关系)。
注意:一次技能只沿一条射线射出,因此反方向(例如 (1,0,0) 与 (-1,0,0))不视为同一方向,需要分别用一次技能击破。
请你求出最少需要释放几次技能,才能击破所有定位靶。

输入描述:

第一行包含三个正整数 n, m, l,分别表示矩阵的深度、行数、列数。
接下来,按照深度优先的顺序输入 n 个二维矩阵块。每个矩阵块有 m 行,每行一个长度为 l 的字符串,仅由字符 H、T、. 组成。
输入保证整个三维矩阵中恰好只有一个 H。

输出描述:

输出一个整数,表示最少需要的技能释放次数。

示例1

输入

1 3 5
T...T
.T.T.
..H..

输出

2

备注:

1 ≤ n, m, l ≤ 100
输入字符仅包含 H、T、.
保证有且仅有一个 H
#include<iostream>
#include<vector>
#include<map>
using namespace std;

#define int long long

struct node
{
    int x, y, z;

    bool operator<(const node& other) const
    {
        if (x != other.x)
            return x < other.x;
        if (y != other.y)
            return y < other.y;
        return z < other.z;
    }
};

int gcd(int x, int y)
{
    return y == 0 ? x : gcd(y, x % y);
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, m, l;
    cin >> n >> m >> l;

    int hx, hy, hz;
    vector<vector<string>> g(n, vector<string>(m));

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cin >> g[i][j];

            for (int k = 0; k < l; k++)
            {
                if (g[i][j][k] == 'H')
                    hx = i, hy = j, hz = k;
            }
        }
    }

    map<node, int> mp;

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            for (int k = 0; k < l; k++)
            {
                if (g[i][j][k] != 'T')
                    continue;

                int x = i - hx;
                int y = j - hy;
                int z = k - hz;

                int d = gcd(gcd(abs(x), abs(y)), abs(z));

                mp[{x / d, y / d, z / d}]++;
            }
        }
    }

    cout << mp.size();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值