题目大意:初始有 n 个盒子 和 一个小球,A 和 B 玩一个游戏。游戏过程如下:
A将小球放在某一个盒子内,然后 B 来猜小球在哪个盒子,每一次B会询问小球是否在 他猜的数字
a
i
a_i
ai 中。游戏共进行 m 轮,A为了不让B赢,在每一次 B 猜的时候 A可以移动小球到相邻位置,游戏开始前和游戏结束后 A 也可以移动小球一次。
设A初始将球放在 x x x 位置,游戏结束后球最后的位置在 y y y,问有多少种点对 ( x , y ) (x,y) (x,y) 是可能的结果。
一个结论是,对于每一个起点,能到达的终点是一段连续的区间。因为小球可以移动也可以等待,假设小球到达的位置是不连续的区间,那么小球总能先到达两边的位置然后等待最后移动到被 ban 的位置。
那么一个做法是,对于每一个位置,只需要找出它能走到的最左边和最右边就能得到答案。
将问题转化为一个 m ∗ n m * n m∗n 的方格图模型,在 ( i , a i ) (i,a_i) (i,ai)上有一个障碍物,初始你在 ( 0 , y ) (0,y) (0,y) 位置,每一次可以向左下,下,右下三个方向移动,不能碰到障碍物,求能走到的最多端和最右端,
考虑只处理能走到的最左端。对于每一个起点,贪心的做法是不停的往左下走,遇到障碍物就向下走。对于每一个障碍物,只会影响到一个点的答案,这个点不得不向下走一次,而向下走一次等价于起点向右移动一次。
用一个数组记录每一个起点向左走的过程中必须向下走几次,将障碍物倒着放,然后递推,求右边界是类似的方法。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
typedef long long ll;
int L[maxn],R[maxn];
int a[maxn],n,m;
const int p = 1e5;
int main() {
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++)
scanf("%d",&a[i]);
for(int i = m; i >= 1; i--) {
L[a[i] + i + p] = L[a[i] + i + 1 + p] + 1;
R[a[i] - i + p] = R[a[i] - i - 1 + p] + 1;
}
ll ans = 0;
for(int i = 1; i <= n; i++) {
int l = max(1,i - m - 1 + L[i + p]);
int r = min(n,i + m + 1 - R[i + p]);
ans += r - l + 1;
}
if(n == 1) ans = 0;
printf("%lld\n",ans);
return 0;
}