zoukankan      html  css  js  c++  java
  • 【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; 
    }
    
  • 相关阅读:
    BadUSB 利用
    java 将函数作为参数传递
    odoo12 修行提升篇之 常用的高阶函数 (二)
    odoo12 修行提升篇之 异步定时任务 (一)
    odoo12 修行基础篇之 利用kanban做分析 点击跳转分析模型列表 (九)
    odoo12 修行基础篇之 kanban (八)
    odoo12 修行基础篇之 记录批处理 (七)
    odoo12 修行基础篇之 列表的筛选和分组 (六)
    odoo12 修行基础篇之 添加记录编码 (五)
    odoo12 修行基础篇之 添加工作流和操作记录 (四)
  • 原文地址:https://www.cnblogs.com/heyujun/p/11806145.html
Copyright © 2011-2022 走看看