• 【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; 
    }
    
  • 相关阅读:
    vim 从嫌弃到依赖(12)——打开及保存文件
    vim 从嫌弃到依赖(11)——标签页操作
    VSCode能编译cpp,无法正常运行、调试,可能原因及解决方案(VSCode,anaconda,环境变量)
    Linux 系统的 Shell 脚本中检查字符串是否包含子字符串
    if...elif...elif...else...if
    MD5加密的4种方式
    英文大小写转换的6种方式
    js脚本混淆&加密(转载)
    java rsa加密
    齐云网关 (自实现网关)
  • 原文地址:https://www.cnblogs.com/heyujun/p/11806145.html
Copyright © 2020-2023  润新知