CF526F Pudding Monsters
题意:
-
给定一个 \(n×n\) 的棋盘,其中有 \(n\)个棋子,每行每列恰好有一个棋子
-
求有多少个 \(k×k\) 的子棋盘中恰好有\(k\) 个棋子
-
\(n\leq3×10^5\)
"所有 \(r_i\) 各不相同,所有 \(c_i\) 也各不相同" \(\Rightarrow\) 各行各列都有且仅有一个点
二维拍扁转化为一维,构造一个序列\(a_{1...n}\),对于一个点\((x,y)\),\(a[x]=y\)
转化为连续段计数问题
寻找满足\(max-min+1=r-l+1\)的区间,即统计\(max-min+l-r=0\)的区间个数
单调栈+线段树动态维护
枚举右端点 \(r\) ,维护所有左端点 \(l\) 的信息,令 \(mn[l]=max-min+l-r\)
随着 \(r\) 变大,\(max\) 最后一个部分会变大,用单调栈维护,转化为 \(O(N))\) 区间减
随着 \(r\) 变大,\(min\) 最后一个部分会变大,用单调栈维护,转化为 \(O(N))\) 区间减
随着 \(r\) 变大,区间减
\(O(N)\) 区间加,查询 \(mn[l]=0\) 的个数
\(mn[l]\geq 0\)
\(O(N)\) 区间加,查询最小值的个数
方法:单调栈+线段树维护 \(O(nlogn)\)
- 每个区间\(l\)值固定,\(i \rightarrow i+1\) ,该区间的\(mn-1\)
- \(max\)通过单调栈维护,若\(a[stk[tp]]<a[i]\),那么更新线段树区间\([stk[tp-1]+1,stk[tp]]\)的\(mn\),\(mn+a[i]-a[stk[tp]]\),弹出栈顶,将\(i\)加入栈中
- \(min\)通过单调栈维护,若\(a[stk[tp]]>a[i]\),那么更新线段树区间\([stk[tp-1]+1,stk[tp]]\)的\(mn\),\(mn+a[stk[tp]]-a[i]\),弹出栈顶,将\(i\)加入栈中
- 同时用\(cnt\)数组维护每个区间内的能取到\(mn\)的个数
- \(up\)更新\(mn\)的时候,注意若\(ls,rs\)的\(mn\)相同,要累加
#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
using namespace std;
const int N=3e5+5;
int n,a[N],x,y,mn[N<<2],tag[N<<2],cnt[N<<2];
int tp1,tp2,stk1[N],stk2[N];
long long ans;
void up(int p){
mn[p]=min(mn[ls],mn[rs]);
cnt[p]=0;
if(mn[p]==mn[ls]) cnt[p]+=cnt[ls];
if(mn[p]==mn[rs]) cnt[p]+=cnt[rs];
}
void build(int p,int L,int R){
if(L==R){
cnt[p]=1;
mn[p]=L;
return ;
}
int mid=L+R>>1;
build(ls,L,mid);
build(rs,mid+1,R);
up(p);
}
void down(int p){
if(!tag[p]) return ;
mn[ls]+=tag[p];
mn[rs]+=tag[p];
tag[ls]+=tag[p];
tag[rs]+=tag[p];
tag[p]=0;
}
long long query(int p,int l,int r,int L,int R){
if(L<=l&&R>=r){
if(mn[p]==0) return cnt[p];
else return 0;
}
down(p);
int mid=l+r>>1;
long long ans=0;
if(mid>=L) ans+=query(ls,l,mid,L,R);
if(mid<R) ans+=query(rs,mid+1,r,L,R);
return ans;
}
void update(int p,int l,int r,int L,int R,int k){
if(L<=l&&R>=r){
mn[p]+=k;
tag[p]+=k;
return ;
}
int mid=l+r>>1;
down(p);
if(mid>=L) update(ls,l,mid,L,R,k);
if(mid<R) update(rs,mid+1,r,L,R,k);
up(p);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>x>>y;
a[x]=y;
}
build(1,1,n);
for(int i=1;i<=n;i++){
update(1,1,n,1,n,-1);
while(tp1&&a[stk1[tp1]]<a[i]){//max
update(1,1,n,stk1[tp1-1]+1,stk1[tp1],a[i]-a[stk1[tp1]]);
tp1--;
}
stk1[++tp1]=i;
while(tp2&&a[stk2[tp2]]>a[i]){//min
update(1,1,n,stk2[tp2-1]+1,stk2[tp2],a[stk2[tp2]]-a[i]);
tp2--;
}
stk2[++tp2]=i;
ans+=query(1,1,n,1,i);
}
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号