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;
}
posted @ 2026-06-05 20:29  Aguanenti  阅读(13)  评论(0)    收藏  举报