小白月赛69

等腰三角形

题目
题意:
根据给定坐标判断能组成多少个等腰三角形。

思路
任选一个点作为等腰三角形的顶点,与其相连的两条边作为两个腰,这样我们就可以枚举所有点作为顶点,然后计算以该顶点为端点的所有边长度,统计所有长度相等的边的数量即为组成三角形的数量。
统三角形计数量:每次先加上对应长度的边的数量,在将对应长度的 cnt 计数加1。原理:任意两条相等边都可以组成一个三角形,则为 C 2 n C^n_2 C2n,刚好等于 1 + 2 + 3 + . . . + ( n − 1 ) 1 + 2 + 3 + ... + (n - 1) 1+2+3+...+(n1)(等差数列求和 ( n − 1 ) ( 1 + n − 1 ) 2 = n ( n − 1 ) 2 = C 2 n \frac{(n - 1)(1 + n - 1)}{2} = \frac{n(n - 1)}{2} = C^n_2 2(n1)(1+n1)=2n(n1)=C2n),则答案可以先加上对应长度的 cnt,再将对应长度的 cnt 计数加1。这样如果有 n 条边相等,则计数一共是 1 + 2 + . . . + ( n − 1 ) 1 + 2 + ... + (n - 1) 1+2+...+(n1)
对于共线问题:以 A 为顶点,对于AB如果有一条长度相等的边与其共线的话,则位置应该在其关于 A 点的反向延长线上,可以通过计算判断是否存在。

vis[nodes[i].x - (nodes[j].x - nodes[i].x) + 1500][nodes[i].y - (nodes[j].y - nodes[i].y) + 1500]

代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
int cnt[2110000]; // 1000 的平方 + 1000 的平方
int vis[5000][5000];
struct node {
    int x, y;
}nodes[3010];
int dist(node a, node b) {
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}
int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i ++) {
        int x, y;
        cin >> x >> y;
        nodes[i].x = x;
        nodes[i].y = y;
        vis[x + 1500][y + 1500] = 1;
    }
    int ans = 0, line = 0;
    for (int i = 0; i < n; i ++) { // 每一次选一个点作为顶点
        for (int j = 0; j < n; j ++) {
            if (i != j) {
                ans += cnt[dist(nodes[i], nodes[j])];
                cnt[dist(nodes[i], nodes[j])] ++;
                if (vis[nodes[i].x - (nodes[j].x - nodes[i].x) + 1500][nodes[i].y - (nodes[j].y - nodes[i].y) + 1500])
                    line ++;
            }
        }
        for (int j = 0; j < n; j ++)
            cnt[dist(nodes[i], nodes[j])] = 0;
    }
    cout << ans - line / 2; // 枚举每个点,计算共线的时候每个点会计算两次 B->A->C   C->A->B   B、C各计算两次。
    return 0;
}

复杂度

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度:很多

旅游

题目
在这里插入图片描述
大意就是找出最小的 p 值,使得牛牛能用自己的钱修权值大于 p 路使整个图连通。
思路
对于 p 值,我们在区间[0, max_val]内进行二分,其中最大值由每条边的权值的最大值决定。对于每一个二分的 p 值,判断牛牛是否能用自己的钱使图连通。
判断方法,遍历所有的边:

  • 使用并查集,并且维护每一个集合的 size,对于边的权值小于 p 的,直接合并为一个集合并更新集合的size
  • 对于道路权值大于 p 的,如果边的两端不在一个集合里,则将其权值加到数组里(这个数组是专门用来存储牛牛需要花钱修的路的权值),一直遍历到所有节点都在一个集合(即整张图连通,size = n),此时在计算数组里的路所需要的维修费用。

维修费用的计算:因为每条路的费用与第几次维修有关,所以采用贪心的思想,先修贵的,即将数组排序并翻转,然后计算总的维修费用,如果小于 c,则说明当前 p 满足要求,返回 true
代码

#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <unordered_map>
#include <vector>
#include <string>
#include <cmath>
#include <algorithm>
#include <limits.h>
#include <map>
#include <unordered_set>


using namespace std;

struct node {
    int x, y, w;
};
int cmp(node a, node b) {
    return a.w < b.w;
}
long long n, m, c;
vector<node> nodes(100010);
int f[40010];
int cnt[40010];
int find(int a) {
    if (a != f[a])
        f[a] = find(f[a]);
    return f[a];
}
bool solve(int p) {
    vector<int> ans;
    long long sum = 0;
    for (int i = 1; i <= n; i ++) {
        f[i] = i;
        cnt[i] = 1;
    }
    int pos = 0;
    for (int i = 0; i < m; i ++)
        if (nodes[i].w <= p) {
            int x = find(nodes[i].x);
            int y = find(nodes[i].y);
            if (x != y) {
                cnt[x] += cnt[y];
                f[y] = x;
            }
            pos = i;
        }
        else {
            int x = find(nodes[i].x);
            int y = find(nodes[i].y);
            if (x != y) {
                ans.push_back(nodes[i].w);
                cnt[x] += cnt[y];
                f[y] = x;
            }
            if (cnt[x] == n) {
                for (int j = 0; j < ans.size(); j ++) {
                    sort(ans.begin(), ans.end());
                    reverse(ans.begin(), ans.end());
                    sum += (j + 1) * ans[j];
                }
                if (sum <= c)
                    return true;
                break;
            }
        }
    return false;
}
int main() {
    cin >> n >> m >> c;
    int t = 0;
    for (int i = 0; i < m; i ++) {
        int x, y, w;
        scanf("%d%d%d", &x, &y, &w);
        nodes[i] = {x, y, w};
        t = max(t, w);
    }
    sort(nodes.begin(), nodes.begin() + m, cmp);
    int l = 0, r = t;
    while (l < r) {
        int mid = l + r >> 1;
        if (solve(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << l;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值