「JOI 2015 Final」城墙 题解 (树状数组)

题目简介

分析

对于每个点 \((i,j)\) 我们获得它向左上方延伸的最大距离 \(l(i,j)\) ,以及向右下方延伸的最大距离 \(r(i,j)\) 。然后我们在对角线上遍历,对于对角线上的点 \((a,b)\) 和点 \((c,d)\) \((a<c,b<d)\),如果满足 \(l(c,d)\ge c-a\) 并且 \(r(a,b)\ge c-a\) 。那么他们就可以围成一个正方形。

于是题目可以转换为:对于每个左上端点 \((i,j)\),在\((i+len−1,j+len−1)(len\in[L,r(i,j)])\)中,求有多少个点\((i+K,j+K)\),使得 \(l(i+len-1,j+len-1)\ge len\)

改写式子:

\[l(c,d)\ge c-a \]

\[a\ge c-l(c,d) \]

使用树状数组,先对对角线上的点进行编号,然后依次将对角线上有用的值加入权值树状数组中,同时查找相应的值,最后在消去无用的贡献,同时在 \((i+r[i][j]-1,j+r[i][j]-1)\) 处添加删除标记,以便后续删除多算进去的值。

详情见代码

\(AC\ Code\)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
using ll=long long;
const int Maxn=4e3+5;
int mat[Maxn][Maxn];
int a[Maxn][Maxn];
int b[Maxn][Maxn];
int l[Maxn][Maxn];
int r[Maxn][Maxn];
int c[Maxn<<1];
int d[Maxn][Maxn];
inline int lowbit(int x){return x&-x;}
inline void add(int x,int val){
	for(;x<=8e3;x+=lowbit(x))c[x]+=val;
}
inline int ask(int x){
	int res=0;
	for(;x;x-=lowbit(x))res+=c[x];
	return res;
}
vector<int>p[Maxn<<1];
int calc(int a,int b,int n,int m,int L){
	memset(c,0,sizeof c);
	ll res=0;if(min(n-a+1,m-b+1)<L)return 0;
	int cnt=0;
	for(int i=a+L-1,j=b+L-1;i<=n&&j<=m;++i,++j)add(i-l[i][j]+1,1),++cnt;
	for(int i=a,j=b;i<=n&&j<=m;++i,++j)d[i][j]=d[i-1][j-1]+1;
	for(int i=a,j=b;i<=n&&j<=m;++i,++j){
		if(r[i][j]>=L){
			res+=ask(i);
			p[d[i+r[i][j]-L][j+r[i][j]-L]].emplace_back(i);
		}
		add(i+L-l[i+L-1][j+L-1],-1),--cnt;
		for(int x:p[d[i][j]])res-=ask(x);p[d[i][j]].clear();
	}
	return res;
}
void solve(int n,int m,int L){
	ll ans=calc(1,1,n,m,L);
	for(int i=2;i<=n;++i)ans+=calc(i,1,n,m,L);
	for(int i=2;i<=m;++i)ans+=calc(1,i,n,m,L);
	cout<<ans<<'\n';
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int n,m,L,p;cin>>n>>m>>L>>p;
	for(int i=1;i<=p;++i){
		int x,y;cin>>x>>y;
		mat[x][y]=1;
	}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			if(mat[i][j])a[i][j]=b[i][j]=0;
			else a[i][j]=a[i][j-1]+1,b[i][j]=b[i-1][j]+1; 
			l[i][j]=min(a[i][j],b[i][j]);
		}
	for(int i=n;i;--i)
		for(int j=m;j;--j){
			if(mat[i][j])a[i][j]=b[i][j]=0;
			else a[i][j]=a[i][j+1]+1,b[i][j]=b[i+1][j]+1;
			r[i][j]=min(a[i][j],b[i][j]);
		}
	solve(n,m,L);
	return 0;
}

$$-----EOF-----$$

posted @ 2022-07-19 20:26  AlienCollapsar  阅读(84)  评论(0编辑  收藏  举报
// 生成目录索引列表 // ref: http://www.cnblogs.com/wangqiguo/p/4355032.html // modified by: zzq