#分治 or 线段树+单调栈#CF526F Pudding Monsters
题目
给定一个 \(n \times n\) 的棋盘,其中有 \(n\) 个棋子,每行每列恰好有一个棋子。
求有多少个 \(k \times k\) 的子棋盘中恰好有 \(k\) 个棋子。
分析
先将二维的棋子转换成一维的排列,也就是问有多少个区间 \([l,r]\) 满足 \(\max-\min=r-l\)
一种方法就是分治,分类讨论最大值最小值在哪边,同一边那么另外一个端点就可以确定,
否则用一个桶记录一下 \(\max-r\) 或者 \(\max+l\) 的个数,把不合法的删掉,这样就是一个 \(\log\)
另外一种方法是固定右端点,然后用单调栈维护后缀最大值和最小值并在线段树上实时修改,
观察到 \(\max-\min+l\geq r\),并且在 \(l=r\) 时必取得最小值 \(r\),那么也就是询问最小值个数,同样在线段树上维护,也是一个 \(\log\)
代码(分治)
#include <cstdio>
#include <cctype>
using namespace std;
const int N=300011; long long ans;
int mn[N],mx[N],a[N],n,c[N<<1];
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
void dfs(int l,int r){
if (l==r) return;
int mid=(l+r)>>1;
mn[mid]=mx[mid]=a[mid];
mn[mid+1]=mx[mid+1]=a[mid+1];
for (int i=mid-1;i>=l;--i) mn[i]=min(mn[i+1],a[i]),mx[i]=max(mx[i+1],a[i]);
for (int i=mid+2;i<=r;++i) mn[i]=min(mn[i-1],a[i]),mx[i]=max(mx[i-1],a[i]);
for (int i=mid;i>=l;--i){
int j=mx[i]-mn[i]+i;
if (j>mid&&j<=r&&mx[i]>mx[j]&&mn[j]>mn[i]) ++ans;
}
for (int j=mid+1;j<=r;++j){
int i=j-mx[j]+mn[j];
if (l<=i&&i<=mid&&mn[i]>mn[j]&&mx[j]>mx[i]) ++ans;
}
int j=mid+1,k=mid+1;
for (int i=mid;i>=l;--i){
for (;j<=r&&mn[j]>mn[i];++j) ++c[mx[j]-j+n];
for (;k<j&&mx[k]<mx[i];++k) --c[mx[k]-k+n];
ans+=c[mn[i]-i+n];
}
for (int i=k;i<j;++i) --c[mx[i]-i+n];
j=mid+1,k=mid+1;
for (int i=mid;i>=l;--i){
for (;j<=r&&mx[j]<mx[i];++j) ++c[mn[j]+j];
for (;k<j&&mn[k]>mn[i];++k) --c[mn[k]+k];
ans+=c[mx[i]+i];
}
for (int i=k;i<j;++i) --c[mn[i]+i];
dfs(l,mid),dfs(mid+1,r);
}
int main(){
n=iut();
for (int i=1,x;i<=n;++i)
x=iut(),a[x]=iut();
dfs(1,n);
return !printf("%lld",ans+n);
}
代码(线段树)
#include <cstdio>
#include <cctype>
using namespace std;
const int N=300011,inf=0x3f3f3f3f; long long ans;
int st0[N],st1[N],Top0,Top1,w[N<<2],mn[N<<2],lazy[N<<2],n,a[N];
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void build(int k,int l,int r){
mn[k]=inf,w[k]=r-l+1;
if (l==r) return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void update(int k,int l,int r,int x,int y,int z){
if (l==x&&r==y){
mn[k]+=z,lazy[k]+=z;
return;
}
int mid=(l+r)>>1;
if (lazy[k]){
mn[k<<1]+=lazy[k],mn[k<<1|1]+=lazy[k];
lazy[k<<1]+=lazy[k],lazy[k<<1|1]+=lazy[k];
lazy[k]=0;
}
if (y<=mid) update(k<<1,l,mid,x,y,z);
else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
else update(k<<1,l,mid,x,mid,z),update(k<<1|1,mid+1,r,mid+1,y,z);
if (mn[k<<1]<mn[k<<1|1]) mn[k]=mn[k<<1],w[k]=w[k<<1];
else mn[k]=mn[k<<1|1],w[k]=w[k<<1|1]+(mn[k<<1]==mn[k<<1|1])*w[k<<1];
}
int main(){
n=iut();
for (int i=1,x;i<=n;++i) x=iut(),a[x]=iut();
build(1,1,n);
for (int i=1;i<=n;++i){
for (;Top0&&a[st0[Top0]]<=a[i];--Top0)
update(1,1,n,st0[Top0-1]+1,st0[Top0],a[i]-a[st0[Top0]]);
for (;Top1&&a[st1[Top1]]>=a[i];--Top1)
update(1,1,n,st1[Top1-1]+1,st1[Top1],a[st1[Top1]]-a[i]);
st0[++Top0]=st1[++Top1]=i,update(1,1,n,i,i,i-inf);
ans+=w[1];
}
return !printf("%lld",ans);
}