[报告]ZJU 3648 Gao the Grid II

Abstract

ZJU 3648 Gao the Grid II

组合计数 不等式

Body

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

Source

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

Description

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

Solution

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

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

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

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

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

现在要求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;
}
原文地址:https://www.cnblogs.com/jffifa/p/2710360.html