【51Nod1555】布丁怪
题面
题目大意:
给你一个(n imes n)的棋盘以及(n)个棋子,每个棋子坐标为((x_i,y_i)),保证棋盘的每一行或一列都有且仅有一个棋子,问你有多少个正方形框住的棋子数数值上等于正方形的边长。
其中(1leq nleq 3 imes 10^5,1leq x_i,y_ileq n)。
题解
首先转化一下题面,题面变为:
给你一个长度为(n)的数组(a),问你有多少个区间([l,r])满足(max_{i=l}^ra_i-min_{i=l}^ra_i+1=r-l+1)。
对于这样子的一个东西,我们考虑分治。
那么我们就是要合并([l,mid],[mid+1,r])这两个区间。
因为和最大/小值有关,我们对于([l,mid])维护后缀(min/max),([mid+1,r])维护前缀(min/max),然后按照最大/小值出现位置分类讨论一下。
- (min,max)在同一边,那么对于每个位置(i)可以确定出一个唯一与之确定的左/右端点,只需对左/右区间分别扫一遍即可。
- (min,max)不在同一边,那么我们假定左边取到(min),右边取到(max),那么一个满足条件的区间需要满足(max-min=r-l),
也就是说(min-l=max-r),可以对于每个(min-l,max-r)放进桶里维护。左(max)右(min)的情况同理。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int MAX_N = 3e5 + 5, T = 3e5;
int N, a[MAX_N], mn[MAX_N], mx[MAX_N];
int bln[MAX_N << 2];
long long ans = 0;
void Div(int l, int r) {
if (l == r) return (void)(++ans);
int mid = (l + r) >> 1;
Div(l, mid), Div(mid + 1, r);
mn[mid] = mx[mid] = a[mid];
for (int i = mid - 1; i >= l; i--) {
mn[i] = min(mn[i + 1], a[i]);
mx[i] = max(mx[i + 1], a[i]);
}
mn[mid + 1] = mx[mid + 1] = a[mid + 1];
for (int i = mid + 2; i <= r; i++) {
mn[i] = min(mn[i - 1], a[i]);
mx[i] = max(mx[i - 1], a[i]);
}
for (int i = mid; i >= l; i--) {
int len = mx[i] - mn[i] + 1;
int pos = mid + (len - (mid - i + 1));
if (pos <= mid || pos > r) continue;
if (mx[pos] >= mn[i] && mx[pos] <= mx[i] &&
mn[pos] >= mn[i] && mn[pos] <= mx[i]) ++ans;
}
for (int i = mid + 1; i <= r; i++) {
int len = mx[i] - mn[i] + 1;
int pos = mid - (len - (i - mid + 1));
if (pos > mid || pos < l) continue;
if (mx[pos] >= mn[i] && mx[pos] <= mx[i] &&
mn[pos] >= mn[i] && mn[pos] <= mx[i]) ++ans;
}
int pl = mid + 1, pr = mid + 1;
for (int i = mid; i >= l; i--) {
while (pr <= r && mn[i] < mn[pr]) bln[mx[pr] - pr + T]++, ++pr;
while (pl < pr && mx[i] > mx[pl]) bln[mx[pl] - pl + T]--, ++pl;
ans += bln[mn[i] - i + T];
}
for (int i = pl; i < pr; i++) bln[mx[i] - i + T]--;
pl = pr = mid;
for (int i = mid + 1; i <= r; i++) {
while (pr >= l && mn[i] < mn[pr]) bln[mx[pr] + pr]++, --pr;
while (pl > pr && mx[i] > mx[pl]) bln[mx[pl] + pl]--, --pl;
ans += bln[mn[i] + i];
}
for (int i = pl; i > pr; i--) bln[mx[i] + i]--;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
N = gi(); for (int i = 1; i <= N; i++) a[gi()] = gi();
Div(1, N);
printf("%lld
", ans);
return 0;
}