题目:CF526F.
题目大意:给定一个
n
∗
n
n*n
n∗n的矩阵,其中
n
n
n个坐标有人且每一行和每一列均只有一人,求
k
∗
k
k*k
k∗k的矩阵中只有
k
k
k个人的矩阵的数量(
k
k
k可以使任何数,不是常数).
1
≤
n
≤
3
∗
1
0
5
1\leq n\leq 3*10^5
1≤n≤3∗105.
首先容易把问题转化为求一个序列 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}=r−l.
这个问题很容易在 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}mx−mn=r−l⇔mx−mn−r+l=0
考虑枚举右端点 r r r,然后维护每一个左端点 l l l对应的 m x − m n − r + l mx-mn-r+l mx−mn−r+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,r−1]的区间的值都
−
1
-1
−1.
2.
m
x
mx
mx会改变,且肯定是连续的一段区间
[
l
′
,
r
−
1
]
[l',r-1]
[l′,r−1]的
m
x
mx
mx改变.
3.
m
n
mn
mn会改变,同理是一段连续的区间
[
l
′
,
r
−
1
]
[l',r-1]
[l′,r−1]改变.
以区间的左端点为下标建立线段树,就可以维护变化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;
}