Pudding Monsters

Pudding Monsters

题目大意

给定一个 \(n \times n\) 的棋盘,其中有 \(n\) 个棋子,每行每列恰好有一个棋子。

求有多少个 \(k \times k\) 的子棋盘中恰好有 \(k\) 个棋子。

\(n \le 3 \times 10^5\)

分析

经典题目。

我们将二维问题变成一维的,对于点\((x,y)\)我们另\(a[x]=y\)

则问题变成了,有多少个区间,其中排序后,其中的数字是连续的。

首先,我们知道如果一个区间其中的数字是连续的,则其满足\(max(a[l...r]) - min(a[l...r] = r - l)\)的。

则我们可以用类似于扫描线或者说二维数点的一种做法。我们枚举右端点,维护左端点的一些值。

假设我们此时枚举到了\(r\),则我们可以设\(f_l = max(a[l...r]) - min(a[l...r]) - (r - l)\),则两个问题。

第一问题,如何维护\(f_l\)这个的话,我们直接观察原式,\(f_l\)的上一个值是\(f_l = max(a_l...a_{r-1}) - min(a_l...a_{r-1} - (r-l-l)\)。因此我们要看max和min的变化,也就是后缀最大值,那就想到利用单调栈,我们维护一个单调递增的,维护一个单调递减的。我们以单调递增,也就是max的变化来举例。假设此时,我们加入一个新的值入栈,其弹出的所有栈中的值,所对应的一段连续区间的所有f值,都要变化。假设弹出的值为\(k\),其对应的区间长度为\(len\),则我们只需要将[k-len+1,k]这段区间全部区间加\(a_r-a_k\)min就是类似的了。最后别忘记,我们还要将区间[1,r-1]全部-1。

另一个问题,我们如何统计\(f_l=0\)的值?。如果是常规的区间加,我们不能通过线段树去找其中0的个数了。但是本题有其特殊性,所有的\(f_l\geq 0\),因此我们直接维护mimicnt即可。请注意,只有最小值是0,我们才会统计答案。

AC_code

#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
using ll = long long;
const int N = 3e5 + 10;

struct Node
{
    int l,r;
    int add,mi,micnt;
}tr[N<<2];

int n;
int a[N];
int stk1[N],tt1,stk2[N],tt2;
int s1[N],s2[N];

void pushup(int u)
{
    tr[u].mi = min(tr[u<<1].mi,tr[u<<1|1].mi);tr[u].micnt = 0;
    if(tr[u].mi==tr[u<<1].mi) tr[u].micnt += tr[u<<1].micnt;
    if(tr[u].mi==tr[u<<1|1].mi) tr[u].micnt += tr[u<<1|1].micnt;
}

void pushdown(int u)
{
    auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
    if(root.add)
    {
        left.add += root.add,left.mi += root.add;
        right.add += root.add,right.mi += root.add;
        root.add = 0;
    }
}

void build(int u,int l,int r)
{
    tr[u] = {l,r};
    if(l==r) 
    {
        tr[u].micnt = 1;
        return ;
    }
    int mid = l + r >> 1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(u);
}

void modify(int u,int l,int r,int k)
{
    // cout<<l<<" "<<r<<" "<<k<<endl;
    if(tr[u].l>r||tr[u].r<l) return ;
    if(l<=tr[u].l&&tr[u].r<=r)
    {
        tr[u].mi += k;
        tr[u].add += k;
        return ;
    }
    pushdown(u);
    modify(u<<1,l,r,k),modify(u<<1|1,l,r,k);
    pushup(u);
}

int query(int u,int l,int r)
{
    if(tr[u].l>r||tr[u].r<l) return 0;
    if(l<=tr[u].l&&tr[u].r<=r) 
    {
        if(!tr[u].mi) return tr[u].micnt;
        return 0;
    }
    pushdown(u);
    return query(u<<1,l,r) + query(u<<1|1,l,r);
}

int main()
{
    ios;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x,y;cin>>x>>y;
        a[x] = y;s1[i] = s2[i] = 1;
    }
    build(1,1,n);
    ll ans = 1;
    stk2[++tt2] = stk1[++tt1] = 1;
    for(int i=2;i<=n;i++)
    {
        modify(1,1,i-1,-1);
        while(tt1&&a[stk1[tt1]]<=a[i]) 
        {
            int t = stk1[tt1];
            s1[i] += s1[t];
            modify(1,t-s1[t]+1,t,a[i] - a[t]);
            tt1--;
        }
        while(tt2&&a[stk2[tt2]]>=a[i])
        {
            int t = stk2[tt2];
            s2[i] += s2[t];
            modify(1,t-s2[t]+1,t,a[t] - a[i]);
            tt2--;
        }
        stk2[++tt2] = stk1[++tt1] = i;
        ans += query(1,1,i);
    }
    cout<<ans<<'\n';
    return 0;
}
posted @ 2022-11-11 16:21  艾特玖  阅读(18)  评论(0编辑  收藏  举报