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\),因此我们直接维护mi
与micnt
即可。请注意,只有最小值是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;
}