Skip to content

Carnival

Written by Raouf Ould Ali.

Problem Statement

We are given lattice points (), with the guarantee that no three points are collinear. For every triple of points , these form a triangle. The score of this triangle is the number of points lying strictly inside it (excluding its vertices). The task is to output, for each possible score , how many triangles contain exactly points.


Key Observations

  • Naïve approach For each triangle , check every other point to see if it lies inside.

    • This is : there are triangles, and each check costs .
    • With , this is too slow.
  • Sorting helps The first step in the solution is to sort the points by their -coordinates.

    • After sorting, any triangle with will have its vertices ordered with respect to the component.
    • This prevents double-counting and ensures that the points_below[i][j] values behave consistently when we combine them.

Precomputation: Points under a segment

For two points and (), consider the directed segment . We count how many points with indices strictly between and lie strictly to the right of this line.

Formally:

This preprocessing costs .


Using the precomputation for triangles

Now, for each triple with , we want to compute the number of points inside the triangle .

  • If the orientation is counter-clockwise:

  • If the orientation is clockwise:

We then increment the counter:


Complexity Analysis

  • Sorting:
  • Precomputation: for filling points_below
  • Triangle enumeration:
  • Total: , which is efficient for .

Memory: for points_below.

Implementation

cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;

int cross_product(int x1, int y1, int x2, int y2)
{
    return x1 * y2 - x2 * y1;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    vector<pair<int, int>> point(n);
    for (int i = 0; i < n; i++)
        cin >> point[i].first >> point[i].second;

    sort(point.begin(), point.end());

    vector<vector<int>> points_below(n, vector<int>(n, 0));

    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
        {
            for (int k = i + 1; k < j; k++)
            {
                if (cross_product(point[j].first - point[i].first,
                                  point[j].second - point[i].second,
                                  point[k].first - point[i].first,
                                  point[k].second - point[i].second) < 0)
                {
                    points_below[i][j] += 1;
                }
            }
        }

    vector<int> O(n - 2, 0);

    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
            for (int k = j + 1; k < n; k++)
            {
                if (cross_product(point[j].first - point[i].first,
                                  point[j].second - point[i].second,
                                  point[k].first - point[i].first,
                                  point[k].second - point[i].second) > 0)
                {
                    O[points_below[i][k] - points_below[i][j] - points_below[j][k] - 1] += 1;
                }
                else
                {
                    O[points_below[i][j] + points_below[j][k] - points_below[i][k]] += 1;
                }
            }

    for (int i = 0; i < n - 2; i++)
        cout << O[i] << ' ';
}