链接

题意:

\(n×m\) 的网格
有 \(k\)个不能走的点,坐标为\((x_i,y_i)\)
我们从\((1,1)\)点出发
一次移动可以向右 / 向下移动一格,最后走到不能走为止。
他有多少个格子是能够走得到的?

分析:

我们看题意可知,出了1这个点其他点如果能到,只能是他左边能到,或者上面能到,当然它本身一定能走,然后我们分析第一行,第一行的话,我们只能走到第一次遇到的那个点,因为后面的既没办法从左边走过来,又没办法从上面走过来,所以第一行可走的是\((1--(y_1-1))\),如果第一行没有点那么更简单,第一行都能走,下面的我们利用上面这个状态。

我们先将当前行可以从左边过来的小区间都搜出来,然后如果上面可以过来肯定下面这个区间的一部分就能过来,
假设上一行能到的区间是\((l_1,r_1)\),当前行从左边可用的区间是\((l_2,r_2)\),那么当前行可走的区间是\((max(l_1,l_2),r_2)\).

int n, m, k;
struct node
{
int x, y;
} q[maxn];
bool cmp(node a, node b)
{
if(a.x != b.x) return a.x < b.x;
return a.y < b.y;
}
std::vector<pii> v[2], now;

void solve()
{
n = read;
m = read;
k = read;
for(int i = 1; i <= k; i++)
{
q[i].x = read;
q[i].y = read;
}
sort(q + 1, q + 1 + k, cmp);

int top = 1;
int flag = 0;
ll ans = 0;
v[flag].clear();
v[flag ^ 1].clear();

v[flag].push_back({1, 1});
for(int i = 1; i <= n; i++)
{
now.clear();
if(q[top].x == i) /// find 行
{
int front = 1;
while(top <= k && q[top].x == i) ///当前行的
{
if(front <= q[top].y - 1)
{
now.push_back({front, q[top].y - 1});
}
front = q[top].y + 1; ///右移
top++;
}
if(front <= m) now.push_back({front, m});
}
else
{
now.push_back({1, m});
}

int num1 = v[flag].size();
int num2 = now.size();
int tmp = 0;
for(int i = 0; i < num2; i++)
{
while(tmp < num1 && v[flag][tmp].y < now[i].x) tmp++;
if(tmp == num1) break;
int l = max(v[flag][tmp].x, now[i].x);
int r1 = min(v[flag][tmp].y, now[i].y);
int r = now[i].y;
if(r1 >= l)
{
v[flag ^ 1].push_back({l, r});
ans += (r - l + 1);
}

}
v[flag].clear();
flag ^= 1;
}
cout << ans << endl;
}