Codeforces 526 F Pudding Monsters题解(线段树+单调栈)

题目:CF526F.
题目大意:给定一个 n ∗ n n*n nn的矩阵,其中 n n n个坐标有人且每一行和每一列均只有一人,求 k ∗ k k*k kk的矩阵中只有 k k k个人的矩阵的数量( k k k可以使任何数,不是常数).
1 ≤ n ≤ 3 ∗ 1 0 5 1\leq n\leq 3*10^5 1n3105.

首先容易把问题转化为求一个序列 a a a中,有多少个子区间 [ l , r ] [l,r] [l,r]满足 max ⁡ i = l r { a i } − min ⁡ i = l r { a i } = r − l \max_{i=l}^{r}\{a_i\}-\min_{i=l}^{r}\{a_i\}=r-l maxi=lr{ai}mini=lr{ai}=rl.

这个问题很容易在 O ( n 2 ) O(n^2) O(n2)的时间复杂度内解决,可是题目的要求明显是 O ( n log ⁡ n ) O(n\log n) O(nlogn)及更优的复杂度.

考虑改变一下转化一下条件式子:
m x = max ⁡ i = l r { a i } , m n = min ⁡ i = l r { a i } m x − m n = r − l ⇔ m x − m n − r + l = 0 mx=\max_{i=l}^{r}\{a_i\},mn=\min_{i=l}^{r}\{a_i\}\\ mx-mn=r-l \Leftrightarrow mx-mn-r+l=0 mx=i=lmaxr{ai},mn=i=lminr{ai}mxmn=rlmxmnr+l=0

考虑枚举右端点 r r r,然后维护每一个左端点 l l l对应的 m x − m n − r + l mx-mn-r+l mxmnr+l.

r r r变成 r + 1 r+1 r+1的时候,显然变化有三种:
1. r r r变成了 r + 1 r+1 r+1,也就是说要给左端点在 [ 1 , r − 1 ] [1,r-1] [1,r1]的区间的值都 − 1 -1 1.
2. m x mx mx会改变,且肯定是连续的一段区间 [ l ′ , r − 1 ] [l',r-1] [l,r1] m x mx mx改变.
3. m n mn mn会改变,同理是一段连续的区间 [ l ′ , r − 1 ] [l',r-1] [l,r1]改变.

以区间的左端点为下标建立线段树,就可以维护变化1了.

至于最值的变化,称一个极大的最值相同的子区间为一段,那么显然每次操作最多增加一个段,然后会将前面的一些段消除变成一段(与增加的段并成一段),容易发现这个时候被被操作的段的总数是线性的.

而显然,段的最大值必然是递减的,而每一段每次的变化量又是相同的(相当于可以直接区间加),所以可以用单调栈维护每一段,并用线段树维护变化量.

然后,答案就是每一次右端点变化时线段树中最小值的出现次数之和了(显然最小值必然为 0 0 0).

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

具体实现的时候,不需要真的在栈中存储每个节点代表的区间和最值,只需要存储一个区间右端点就可以通过一些操作得到其它信息了.

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=300000,INF=(1<<30)-1;

int n,a[N+9];
struct tree{
  int min,add,cnt;
}tr[N*4+9];

void Pushup(int k){
  int ls=k<<1,rs=k<<1|1;
  tr[k].min=min(tr[ls].min,tr[rs].min);
  tr[k].cnt=(tr[ls].min==tr[k].min?tr[ls].cnt:0)+(tr[rs].min==tr[k].min?tr[rs].cnt:0); 
}

void Update_add(int k,int v){tr[k].min+=v;tr[k].add+=v;}

void Pushdown(int k){
  if (!tr[k].add) return;
  Update_add(k<<1,tr[k].add);Update_add(k<<1|1,tr[k].add);
  tr[k].add=0;
}

void Build(int L,int R,int k){
  if (L==R) {tr[k].cnt=1;return;}
  int mid=L+R>>1;
  Build(L,mid,k<<1);Build(mid+1,R,k<<1|1);
  Pushup(k);
}

void Change_add(int L,int R,int v,int l,int r,int k){
  if (L>R) return;
  if (L==l&&R==r) {Update_add(k,v);return;}
  Pushdown(k);
  int mid=l+r>>1;
  if (R<=mid) Change_add(L,R,v,l,mid,k<<1);
  else if (L>mid) Change_add(L,R,v,mid+1,r,k<<1|1);
    else Change_add(L,mid,v,l,mid,k<<1),Change_add(mid+1,R,v,mid+1,r,k<<1|1);
  Pushup(k);
}

int Query_cnt(int L,int R,int l,int r,int k){
  if (L>R) return 0;
  if (L==l&&R==r) return tr[k].min==0?tr[k].cnt:0;
  Pushdown(k);
  int mid=l+r>>1;
  if (R<=mid) return Query_cnt(L,R,l,mid,k<<1);
  else if (L>mid) return Query_cnt(L,R,mid+1,r,k<<1|1);
    else return Query_cnt(L,mid,l,mid,k<<1)+Query_cnt(mid+1,R,mid+1,r,k<<1|1);
}

int mx[N+9],mn[N+9],cmx,cmn;
LL ans;

Abigail into(){
  scanf("%d",&n);
  int x;
  for (int i=1;i<=n;++i){
    scanf("%d",&x);
    scanf("%d",&a[x]);
  }
}

Abigail work(){
  Build(1,n,1);
  for (int i=1;i<=n;++i){
  	Change_add(1,i-1,-1,1,n,1);
	for (;cmx&&a[mx[cmx]]<=a[i];--cmx)
	  Change_add(mx[cmx-1]+1,mx[cmx],a[i]-a[mx[cmx]],1,n,1);
	mx[++cmx]=i;
	for (;cmn&&a[mn[cmn]]>=a[i];--cmn)
	  Change_add(mn[cmn-1]+1,mn[cmn],a[mn[cmn]]-a[i],1,n,1);
	mn[++cmn]=i;
	ans+=(LL)Query_cnt(1,i,1,n,1);
  }
}

Abigail outo(){
  printf("%lld\n",ans);
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值