[报告]ZJU 3648 Gao the Grid II

Abstract

ZJU 3648 Gao the Grid II

组合计数 不等式

 

Body

[报告]ZJU 3648 Gao the Grid II

第一次把一场浙大月赛的题目全部做完呢~对于小女这样的菜鸟来说,算是不小的成就,好开心呀~

Source

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3648

Description

求N x M的矩形网格中有多少个以格点为顶点的锐角三角形。

Solution

据说本题数据很水,可以O(N^4)乱搞过去。不过这里还是说说正经做法。

首先注意到任意一个三角形可以唯一确定一个包含它的最小矩形,并且三角形至少有一个顶点在矩形的顶点上。

然后可以发现,对于任意的锐角三角形,三个顶点一定都在矩形的边上。

如果我们知道给定大小的矩形上有多少个锐角三角形,我们就可以枚举矩形大小,算出大矩形内有多少个小矩形,乘上小矩形上的锐角三角形个数,最后再求和就可以了。

现在问题就是求给定大小的矩形上有多少个锐角三角形。设矩形大小为(i, j),不妨令三角形的一个顶点在(i, j)上,如图。

[报告]ZJU 3648 Gao the Grid II

现在要求alpha和beta都是锐角,用向量点积或者余弦定理都可以解出q*j>p*(i-p)以及p*i>q*(j-q),且0<p<=i以及0<q<j。枚举p的话,q的解集是可以O(1)算出来的(一个一次不等式解集和一个二次不等式解集的交)。顶点在其它三个点的情况类似。

这样对于给定的(i, j),锐角三角形的个数就可以O(N)求出。所以对于所有的(i, j),只需要O(N^3)时间预处理统计。

具体实现时候,我用了当p递增时关于q的二次不等式解集的两个边界一增一减的性质,这样可以避免浮点运算和一大堆乱七八糟的讨论。

Code

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;



typedef long long ll;



int N, M;

ll cnt[111][111];



ll solve(int i, int j) {

    ll res = 0;

    int p, q, l = 0, r = j;

    for (p = 1; p <= i; ++p) {

        while (l<j && l<=r && p*i>l*(j-l)) ++l;

        while (r>0 && l<=r && p*i>r*(j-r)) --r;

        q = p*(i-p)/j;

        if (q >= r) res += max(0, j-q-1);

        else if (q >= l) res += max(0, j-r-1);

        else res += max(0, j-r-1)+max(0, l-q-1);

    }

    return res;

}



int main() {

    int i, j, p, q, l, r;

    for (i = 1; i <= 100; ++i)

        for (j = 1; j <= 100; ++j)

            cnt[i][j] = solve(i, j)+solve(j, i)<<1;

    while (cin>>N>>M) {

        ll ans = 0;

        for (i = 1; i <= N; ++i)

            for (j = 1; j <= M; ++j)

                ans += cnt[i][j]*(N-i+1)*(M-j+1);

        cout<<ans<<'\n';

    }

    return 0;

}

你可能感兴趣的:(grid)