题目大意

二维平面,屏幕是 (0,1)(0,m)(0, 1)–(0, m) 的线段,有nnmm列座位在屏幕前面,是坐标范围 1xn,1ym1 ≤ x ≤ n, 1 ≤ y ≤ m 的整点。有kk个座位已经有人,求出到屏幕的视线不被任何人挡住的座位数量。 题目链接 一个人挡住的区域是它与屏幕两端连线所形成的夹角区域。 alt

可见视线遮挡是被两条线段确定的,但是实际上,单条连线也能够确定视线遮挡的范围。,对于一条线,水平向右的直线和它自身形成的夹角,是一定被视线遮挡的,而两条线确定的,刚好就是这两个相邻区域的并集。 alt

对于当前行,能够覆盖该行的,通过左下角的线,一定是在该行以下,而此时显然斜率越大的线,覆盖的越多。! alt

所以我们逐行处理的时候,保存斜率最大的直线。

对于左下角的直线y=i1xnowx+1,x=xnow1y1y = \frac{i - 1}{x_{now}} * x + 1, x = \frac{x_{now}-1}{y-1}

xnowx_{now}表示斜率最大的线来在第nownow行,且xx坐标为xnowx_{now}

代码中在分子又额外减一是为了防止结果刚好在整数点上,导致计算错误,因为这个点实际上也是被遮挡的。

右上角的线也同理

我们计算每一行没被覆盖的点。然后计算出左上,左下的线分别的结果。然后取min即可。

代码

#include <cstdio>
#include <iostream>
using namespace std;

const int maxN = 2e5 + 7;

int n, m, k, q, x[maxN], y[maxN], minY[maxN], con[maxN][2];

int main()
{
    scanf("%d%d%d%d", &n, &m, &k, &q);
    for(int i = 1; i <= k; ++i) 
        scanf("%d%d", &x[i], &y[i]);
    while(q--) {
        int num; scanf("%d", &num);
        scanf("%d%d", &x[num], &y[num]);
        for(int i = 1; i <= m; ++i)
            minY[i] = n + 1;
        for(int i = 1; i <= k; ++i)
            minY[y[i]] = min(minY[y[i]], x[i]);
        int now = 1; con[1][0] = minY[1] - 1;
        for(int i = 2; i <= m; ++i) {
            if((long long) (now - 1) * minY[i] < (long long) (i - 1) * minY[now]) 
                now = i;
            con[i][0] =  ((long long) (i - 1) * minY[now] - 1) / (now - 1);
        }
        now = m; con[m][1] = minY[m] - 1;   
        for(int i = m - 1; i >= 1; --i) {
            if((long long) (m - now) * minY[i] < (long long) (m - i) * minY[now])
                now = i;
            con[i][1] = ((long long) (m - i) * minY[now] - 1) / (m - now);
        }
        long long ans = 0;
        for(int i = 1; i <= m; ++i)
            ans += min(con[i][0], con[i][1]);
        printf("%lld\n", ans);
    }
    return 0;
}