zoukankan      html  css  js  c++  java
  • 【CF526F】Pudding Monsters(分治套路题)

    点此看题面

    • 一个(n imes n)的正方形,其中每行每列恰有一个点。
    • 问有多少子正方形,也满足每行每列恰有一个点。
    • (nle3 imes10^5)

    二维化一维

    显然这道题很容易想到降维。

    既然每行只有一个点,我们可以用(a_i)表示第(i)行的点在第几列。

    现在问题就变成有多少区间满足其中元素排序之后值连续。

    则区间([l,r])合法的充要条件就是(max_{i=l}^ra_i-min_{i=l}^ra_i=r-l)

    分治套路题

    对于当前分治区间([l,r]),设其中点为(mid),那么([l,mid])([mid+1,r])两区间各自的答案可以直接递归处理。

    接着我们先预处理出(Mx,Mn)两个数组,当(iin[l,mid])时表示([i,mid])的最值,当(iin[mid+1,r])时表示([mid+1,i])的最值。

    现在我们就要考虑如何统计跨中点的区间的答案,分成两类情况。

    两最值在中点同侧

    假设在([l,mid])中。

    我们枚举左端点(i),此时最值为(Mx_i,Mn_i),所以满足(Mx_i-Mn_i=j-i),即(j=Mx_i-Mn_i+i)

    那么我们只要验证一下(j)是不是一个合法的右端点即可。

    两最值在中点异侧

    假设最小值在([l,mid])中,最大值在([mid+1,r])中。

    我们从大到小枚举(i),同时开(p,q)两个指针维护出区间([p,q])表示合法右端点(j)的取值区间,显然这东西随着(i)的移动是单调右移的。

    此时最值为(Mx_j,Mn_i),故(Mx_j-Mn_i=j-i),即(Mn_i-i=Mx_j-j)

    那么只要开桶记一下(Mx_j-j=x)(j)的个数,就可以了。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300000
    #define LL long long
    using namespace std;
    int n,a[N+5];LL ans;
    int Mx[N+5],Mn[N+5],c[2*N+5];I void Solve(CI l,CI r)//分治
    {
    	if(l==r) return (void)++ans;RI i,p,q,mid=l+r>>1;Solve(l,mid),Solve(mid+1,r);//递归子区间
    	Mx[mid]=Mn[mid]=a[mid],Mx[mid+1]=Mn[mid+1]=a[mid+1];
    	for(i=mid-1;i>=l;--i) Mx[i]=max(Mx[i+1],a[i]),Mn[i]=min(Mn[i+1],a[i]);//预处理左区间后缀最值
    	for(i=mid+2;i<=r;++i) Mx[i]=max(Mx[i-1],a[i]),Mn[i]=min(Mn[i-1],a[i]);//预处理右区间前缀最值
    	for(i=l;i<=mid;++i) p=i+Mx[i]-Mn[i],mid<p&&p<=r&&Mn[i]<Mn[p]&&Mx[p]<Mx[i]&&++ans;//最值都在左区间
    	for(i=mid+1;i<=r;++i) p=i-Mx[i]+Mn[i],l<=p&&p<=mid&&Mn[i]<Mn[p]&&Mx[p]<Mx[i]&&++ans;//最值都在右区间
    	for(i=mid,p=q=mid+1;i>=l;ans+=c[Mn[i]-i+n],--i)//最小值在左区间,最大值在右区间
    		{W(q<=r&&Mn[q]>Mn[i]) ++c[Mx[q]-q+n],++q;W(p^q&&Mx[p]<Mx[i]) --c[Mx[p]-p+n],++p;}//p,q维护合法右端点区间
    	W(p^q) --c[Mx[p]-p+n],++p;//清空
    	for(i=mid,p=q=mid+1;i>=l;ans+=c[Mx[i]+i],--i)//最大值在左区间,最小值在右区间
    		{W(q<=r&&Mx[q]<Mx[i]) ++c[Mn[q]+q],++q;W(p^q&&Mn[p]>Mn[i]) --c[Mn[p]+p],++p;}
    	W(p^q) --c[Mn[p]+p],++p;//清空
    }
    int main()
    {
    	RI i,x,y;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&x,&y),a[x]=y;//二维化一维
    	return Solve(1,n),printf("%lld
    ",ans),0;
    }
    
  • 相关阅读:
    【51NOD 1478】括号序列的最长合法子段
    【BZOJ 3527】【ZJOI 2014】力
    【BZOJ 2194】快速傅立叶之二
    【CodeVS 3123】高精度练习之超大整数乘法 &【BZOJ 2197】FFT快速傅立叶
    【BZOJ 2693】jzptab
    【BZOJ 2154】Crash的数字表格
    【BZOJ 3529】【SDOI 2014】数表
    【BZOJ 2820】YY的GCD
    【BZOJ 2301】【HAOI 2011】Problem b
    【POJ 3294】Life Forms 不小于k个字符串中的最长子串
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF526F.html
Copyright © 2011-2022 走看看