【51Nod1555】布丁怪

【51Nod1555】布丁怪

题面

51Nod

题目大意:

给你一个\(n\times n\)的棋盘以及\(n\)个棋子,每个棋子坐标为\((x_i,y_i)\),保证棋盘的每一行或一列都有且仅有一个棋子,问你有多少个正方形框住的棋子数数值上等于正方形的边长。

其中\(1\leq n\leq 3\times 10^5,1\leq x_i,y_i\leq 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\n", ans); 
    return 0; 
}
posted @ 2019-11-06 16:29  heyujun  阅读(...)  评论(... 编辑 收藏