UVa 12651 Triangles

题目描述

给定一个圆上的 NNN 个点,以及相邻点之间的圆弧长度。你需要计算能够用这些点作为顶点构造出的不同的等边三角形的数量。

输入的第一行是一个整数 NNN,表示点的数量。第二行是 NNN 个整数 XiX_iXi,表示相邻点之间的弧长(按顺序 X1X_1X1 是点 111 到点 222 的弧长,XNX_NXN 是点 NNN 到点 111 的弧长)。

输出一个整数,表示可以构造的不同的等边三角形的数量。

数据范围

  • 3≤N≤1053 \leq N \leq 10^53N105
  • 1≤Xi≤1031 \leq X_i \leq 10^31Xi103

题目分析

1. 等边三角形在圆上的性质

在圆上构造等边三角形,意味着三个顶点必须将圆周三等分。也就是说,如果圆周总长为 CCC,那么三个顶点之间的圆弧长度必须都是 C/3C/3C/3

因此,如果从某个点 ppp 出发,沿着圆走 C/3C/3C/3 的弧长到达点 qqq,再走 C/3C/3C/3 到达点 rrr,再走 C/3C/3C/3 回到 ppp,那么 p,q,rp, q, rp,q,r 就构成一个等边三角形。


2. 基本思路

  1. 计算总周长
    C=∑i=1NXi C = \sum_{i=1}^{N} X_i C=i=1NXi
    如果 CCC 不能被 333 整除,那么不可能构造任何等边三角形,直接输出 000

  2. 前缀和表示点的位置
    我们可以将圆展开,用前缀和表示每个点相对于起点 000 的位置(弧长)。
    设:
    prefix[0]=0 prefix[0] = 0 prefix[0]=0
    prefix[i]=X1+X2+⋯+Xifor 1≤i≤N prefix[i] = X_1 + X_2 + \dots + X_i \quad \text{for } 1 \leq i \leq N prefix[i]=X1+X2++Xifor 1iN
    注意 prefix[N]=Cprefix[N] = Cprefix[N]=C

  3. 判断三等分点是否存在
    对于每个起点 iii(位置为 prefix[i]prefix[i]prefix[i]),我们希望找到另外两个点 jjjkkk,使得:
    prefix[j]≡prefix[i]+C3(modC) prefix[j] \equiv prefix[i] + \frac{C}{3} \pmod{C} prefix[j]prefix[i]+3C(modC)
    prefix[k]≡prefix[i]+2C3(modC) prefix[k] \equiv prefix[i] + \frac{2C}{3} \pmod{C} prefix[k]prefix[i]+32C(modC)
    如果这两个位置都存在于前缀和集合中,则 (i,j,k)(i, j, k)(i,j,k) 构成一个等边三角形。

  4. 避免重复计数
    每个三角形会被它的三个顶点各作为起点统计一次,因此最后的总数需要除以 333


3. 算法步骤

  • 读入 NNNXiX_iXi,计算总周长 CCC
  • 如果 C%3≠0C \% 3 \neq 0C%3=0,输出 000 并结束。
  • 计算前缀和数组 prefixprefixprefix
  • 将前缀和存入哈希集合,以便 O(1)O(1)O(1) 查找。
  • 遍历每个起点 iii,检查 prefix[i]+C/3prefix[i] + C/3prefix[i]+C/3prefix[i]+2C/3prefix[i] + 2C/3prefix[i]+2C/3 是否在集合中(取模 CCC)。
  • 统计满足条件的起点数量,最后除以 333 得到答案。

4. 复杂度分析

  • 时间复杂度:O(N)O(N)O(N),因为每个点只遍历一次,哈希查找是 O(1)O(1)O(1)
  • 空间复杂度:O(N)O(N)O(N),用于存储前缀和和哈希集合。

代码

// Triangles
// UVa ID: 12651
// Verdict: Accepted
// Submission Date: 2025-11-05
// UVa Run Time: 0.060s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <iostream>
#include <vector>
#include <unordered_set>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    while (cin >> N) {
        vector<int> X(N);
        long long total = 0;
        for (int i = 0; i < N; ++i) {
            cin >> X[i];
            total += X[i];
        }

        // 如果总周长不能被3整除,无法构造等边三角形
        if (total % 3 != 0) {
            cout << "0\n";
            continue;
        }

        // 计算前缀和
        vector<long long> prefix(N + 1, 0);
        for (int i = 0; i < N; ++i) {
            prefix[i + 1] = prefix[i] + X[i];
        }

        // 将前缀和存入哈希集合,方便快速查找
        unordered_set<long long> prefixSet(prefix.begin(), prefix.end());

        long long third = total / 3;
        int count = 0;

        // 遍历每个起点
        for (int i = 0; i < N; ++i) {
            long long target1 = prefix[i] + third;
            long long target2 = prefix[i] + 2 * third;

            // 处理循环,取模 total
            if (target1 > total) target1 -= total;
            if (target2 > total) target2 -= total;

            // 如果两个目标位置都存在,则构成三角形
            if (prefixSet.count(target1) && prefixSet.count(target2)) {
                count++;
            }
        }

        // 每个三角形被三个起点各统计一次
        cout << count / 3 << "\n";
    }

    return 0;
}

总结

本题的关键在于理解圆上等边三角形的几何性质:三个顶点必须将圆周三等分。利用前缀和和哈希集合,我们可以高效地判断是否存在这样的三个点,并避免重复计数。算法的时间复杂度为 O(N)O(N)O(N),可以处理 N≤105N \leq 10^5N105 的大数据范围。

### 使用 `GL_TRIANGLES` 模式绘制三角形 在 OpenGL 中,`GL_TRIANGLES` 是一种用于定义多个独立三角形的方式。当使用此模式时,每三个连续的顶点构成一个单独的三角形[^1]。 对于 `glDrawArrays(GL_TRIANGLES, startIndex, count)` 函数调用: - 参数 `startIndex` 表示从哪个索引位置开始读取顶点数据。 - 参数 `count` 定义要处理多少个顶点;由于每个三角形由三个顶点组成,因此实际绘制的三角形数量等于 `count / 3` 向下取整的结果。 下面是一个简单的例子来展示如何利用 C++ 和 OpenGL 来创建并渲染两个相连但不共享任何边界的三角形: ```cpp // 假设已经初始化好了OpenGL环境,并绑定了VBO和VAO等必要对象 float vertices[] = { // 第一个三角形 0.5f, 0.5f, 0.0f, // 右上角 0.5f, -0.5f, 0.0f, // 右下角 -0.5f, -0.5f, 0.0f, // 左下角 // 第二个三角形 -0.5f, -0.5f, 0.0f, // 左下角 -0.5f, 0.5f, 0.0f, // 左上角 0.5f, 0.5f, 0.0f // 右上角 }; GLuint VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 设置顶点属性指针... glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 渲染命令 glDrawArrays(GL_TRIANGLES, 0, 6); // 此处我们有六个顶点,形成两个三角形 ``` 这段代码片段展示了怎样通过指定六组坐标值(即两个分离的三角形),并通过一次绘图指令完成它们的同时显示。值得注意的是,在这种情况下,即使第二个三角形的第一个顶点与第一个三角形最后一个相同,也必须重复提供该顶点的信息,因为这些三角形之间没有任何连接关系[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值