【51Nod1555】布丁怪

【51Nod1555】布丁怪

题面

51Nod

题目大意:

给你一个(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; 
}
原文地址:https://www.cnblogs.com/heyujun/p/11806145.html