题意
- 现在有一个长度为 n 的数组 p,数组元素的取值为 1~n,
- 给我们 m 个限制条件 <x, y> ,每个限制为 p [x] != y.
- 现在让我求出四元组的 <A,B,L,R> 的数量,四元组的意思是在 p 数组中 [A,B] 子区间的任意数字可以取值为 [L, R] 中的任意一个数字。(A、B、L、R 可以均 <= n)。
思路
- 设状态转移数组:f [i][j] 表示以 i 位置为结尾的所有子区间中,以 j 数字结尾所能产生的答案的贡献数量。
- 可以用单调栈去维护状态转移。具体看代码吧更好理解
代码
#include <bits/stdc++.h>
using namespace std;
#define db double
#define ll long long
#define Pir pair<int, int>
#define fi first
#define se second
#define pb push_back
#define m_p make_pair
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
const int N = 5005;
int n, m;
int a[N][N], b[N][N];
ll f[N];
int st[N], top;
int main()
{
scanf("%d %d", &n, &m);
int x, y;
while (m --)
{
scanf("%d %d", &x, &y);
a[x][y] = 1;
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
if (a[i][j]) b[i][j] = 0;
else b[i][j] = b[i - 1][j] + 1;
}
}
ll ans = 0;
for (int i = 1; i <= n; i ++) {
top = 0;
for (int j = 1; j <= n; j ++) {
while (top && b[i][j] <= b[i][st[top]]) top --;
int p = top ? st[top] : 0;
f[j] = (j - p) * b[i][j] + f[p];
ans += f[j];
st[++ top] = j;
}
}
printf("%lld\n", ans);
return 0;
}