CF526F Pudding Monsters
Link
Solution
如果一个 \(k*k\) 的矩阵里放满了 \(k\) 个数,容易发现横坐标和纵坐标都是连续的,我们需要利用好这个性质,那么不妨按横坐标排序,之后只有相连的几个点会造成贡献。对于一个区间 \([l,r]\),如果它是合法的,当且仅当区间长度等于值域长度(由于合法时值域一定是连续的),即
\[r-l+1=\max \limits_{l\leq i\leq r} \{y_i\}-\min \limits_{l\leq i\leq r} \{y_i\}+1
\]
对于一个确定的 \(r\) 只需要统计方程
\[\max \limits_{l\leq i\leq r} \{y_i\}-\min \limits_{l\leq i\leq r} \{y_i\}-r+l=0
\]
成立的个数。可以用线段树对每一个位置维护这个值。考虑到没有重复的数,值域长度一定大于等于区间长度,那么上式左边会恒大于等于 0,而 \(r\) 这个单独的位置一定有一个合法解,即会出现 0,所以只需统计区间最小值(0)出现次数就行了。
对于线段树的维护,考虑增量法。当从 \(r\) 转移到 \(r+1\),\(r\) 会加 1,那么 \([1,r]\) 的所有位置的数减 1。对于 \(min\) 和 \(max\) 用单调栈维护一下,弹栈时对数列更新一下最大值最小值信息即可。
考虑到每次弹栈入栈会 \(modify\) 一次,所以复杂度 \(O(n\log n)\)
#include<stdio.h>
#define lid id<<1
#define rid id<<1|1
#define N 300007
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
int s[N<<2],num[N<<2],tag[N<<2];
void build(int id,int lf,int rf){
s[id]=1;
if(lf==rf) return ;
int mid=(lf+rf)>>1;
build(lid,lf,mid);
build(rid,mid+1,rf);
s[id]=s[lid]+s[rid];
}
inline int min(int x,int y){return x<y? x:y;}
void push(int id,int v){num[id]+=v,tag[id]+=v;}
void pushdown(int id){push(lid,tag[id]),push(rid,tag[id]),tag[id]=0;}
void modify(int id,int lf,int rf,int l,int r,int val){
if(l<=lf&&rf<=r) push(id,val);
else{
int mid=(lf+rf)>>1;
if(tag[id]) pushdown(id);
if(l<=mid) modify(lid,lf,mid,l,r,val);
if(r>mid) modify(rid,mid+1,rf,l,r,val);
num[id]=min(num[lid],num[rid]);
s[id]=(num[id]==num[lid]? s[lid]:0)+(num[id]==num[rid]? s[rid]:0);
}
}
int n,ma[N],mi[N],t1=0,t2=0,a[N];
int main(){
n=read();
for(int i=1;i<=n;i++) a[read()]=read();
build(1,1,n);
long long ans=0;
for(int i=1;i<=n;i++){
modify(1,1,n,1,i,-1);
while(t1&&a[ma[t1]]<a[i])
modify(1,1,n,ma[t1-1]+1,ma[t1],a[i]-a[ma[t1]]),t1--;
while(t2&&a[mi[t2]]>a[i])
modify(1,1,n,mi[t2-1]+1,mi[t2],a[mi[t2]]-a[i]),t2--;
ma[++t1]=mi[++t2]=i;
ans+=1LL*s[1];
}
printf("%lld",ans);
}