题意:
给定一个n*m的矩阵,矩阵元素由X和O构成,请求出其中最大的由X构成的蝴蝶形状。
由X构成的蝴蝶形状的定义如下:
存在一个中心点,并且其往左上、左下、右上、右下四个方向扩展相同的长度(扩展的长度上都是X),且左上顶点与左下顶点、右上顶点与右下顶点之间的格子全由X填充。我们不在意在蝴蝶形状内部是X还是O。
例如:
XOOOX
XXOXX
XOXOX
XXOXX
XOOOX
是一个X构成的蝴蝶形状。
X
也是。
而
XOOX
OXXO
OXXO
XOXX
不是(不存在中心点)。
输出:一行一个整数表示最大的由X构成的蝴蝶形状的对角线的长度。
思路:
很巧妙的题目转化方式!
首先
O
(
n
m
)
O(nm)
O(nm)预处理三个量:
d
p
[
0
]
[
i
]
[
j
]
dp[0][i][j]
dp[0][i][j]:从
(
i
,
j
)
(i,j)
(i,j)的位置开始左下方向连续的
X
X
X的个数。
d
p
[
1
]
[
i
]
[
j
]
dp[1][i][j]
dp[1][i][j]:从
(
i
,
j
)
(i,j)
(i,j)的位置开始向下连续的
X
X
X的个数。
d
p
[
2
]
[
i
]
[
j
]
dp[2][i][j]
dp[2][i][j]:从
(
i
,
j
)
(i,j)
(i,j)的位置开始右下方向连续的
X
X
X的个数。
考虑枚举每一个蝴蝶形状的左上角顶点位置
(
i
,
j
)
(i,j)
(i,j)。
则我们需要找到一个最大的
k
k
k,使
(
i
,
k
)
(i,k)
(i,k)为一个蝴蝶形状的右上顶点位置。
当蝴蝶形状合法时,
k
k
k需要满足的条件为:(蝴蝶形状的长为
k
−
j
+
1
k-j+1
k−j+1)
1
)
.
m
i
n
(
d
p
[
1
]
[
i
]
[
j
]
,
d
p
[
2
]
[
i
]
[
j
]
)
>
=
k
−
j
+
1
1).min(dp[1][i][j],dp[2][i][j]) >= k-j+1
1).min(dp[1][i][j],dp[2][i][j])>=k−j+1
2
)
.
m
i
n
(
d
p
[
0
]
[
i
]
[
k
]
,
d
p
[
1
]
[
i
]
[
k
]
)
>
=
k
−
j
+
1
2).min(dp[0][i][k],dp[1][i][k]) >= k-j+1
2).min(dp[0][i][k],dp[1][i][k])>=k−j+1
3
)
.
k
>
=
j
3).k>=j
3).k>=j
移项后得到:
1
)
.
j
<
=
k
<
=
m
i
n
(
d
p
[
1
]
[
i
]
[
j
]
,
d
p
[
2
]
[
i
]
[
j
]
)
+
j
−
1
1). j <= k <= min(dp[1][i][j],dp[2][i][j]) + j - 1
1).j<=k<=min(dp[1][i][j],dp[2][i][j])+j−1
2
)
.
m
i
n
(
d
p
[
0
]
[
i
]
[
k
]
,
d
p
[
1
]
[
i
]
[
k
]
)
−
k
−
1
>
=
−
j
2).min(dp[0][i][k],dp[1][i][k]) - k - 1 >= -j
2).min(dp[0][i][k],dp[1][i][k])−k−1>=−j
因为需要最大化 k k k,故可以考虑用线段树维护,对于每一个 k k k,线段树的相应节点保存 m i n ( d p [ 0 ] [ i ] [ k ] , d p [ 1 ] [ i ] [ k ] ) − k − 1 min(dp[0][i][k],dp[1][i][k]) - k - 1 min(dp[0][i][k],dp[1][i][k])−k−1。故题目便转化成了在区间 ( j , m i n ( d p [ 1 ] [ i ] [ j ] , d p [ 2 ] [ i ] [ j ] ) + j − 1 ) (j,min(dp[1][i][j],dp[2][i][j]) + j - 1) (j,min(dp[1][i][j],dp[2][i][j])+j−1)中找到最大的下标使其维护的值 > = − j >=-j >=−j。
因为蝴蝶形状有中心点,故长度 k − j + 1 k-j+1 k−j+1一定为奇数,需要两个线段树,对于奇偶性不同的 k k k分开维护。
另外
W
a
n
n
a
f
l
y
Wannafly
Wannafly群还提供了另外一个不错的维护方式。
将
k
k
k按照
k
−
m
i
n
(
d
p
[
0
]
[
i
]
[
k
]
,
d
p
[
1
]
[
i
]
[
k
]
)
+
1
k-min(dp[0][i][k],dp[1][i][k])+1
k−min(dp[0][i][k],dp[1][i][k])+1排序,然后从左到右枚举
j
j
j时,一点点把
k
k
k加入考虑的范畴。
稍微改一改线段树的维护方式也可以搞出来。
代码:
维护方式
1
:
658
m
s
1:658ms
1:658ms
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
#define lson rt<<1
#define rson rt<<1|1
/*
j <= k <= min(dp[1][i][j],dp[2][i][j]) + j - 1
Max{k} && min(dp[0][i][k],dp[1][i][k]) - k - 1 >= -j
*/
const int INF = 1e9 + 7;
const int A = 2e3 + 10;
class TNode{
public:
int l,r,Mx;
};
class Seg_Tree{
public:
TNode Tree[A<<3];
void push_up(int rt){
Tree[rt].Mx = max(Tree[lson].Mx,Tree[rson].Mx);
}
void build_Tree(int rt,int l,int r){
Tree[rt].l = l;
Tree[rt].r = r;
Tree[rt].Mx = -INF;
if(l == r) return;
int mid = (l+r)>>1;
build_Tree(lson,l,mid);
build_Tree(rson,mid+1,r);
push_up(rt);
}
void update(int rt,int pos,int add){
int l = Tree[rt].l,r = Tree[rt].r;
if(l==r){
Tree[rt].Mx = add;
return;
}
int mid = (l+r)>>1;
if(pos<=mid) update(lson,pos,add);
else update(rson,pos,add);
push_up(rt);
}
int query(int rt,int st,int ed,int v){
int l = Tree[rt].l,r = Tree[rt].r;
if(l == r) return l;
int res = 0;
if(st<=l && r<=ed){
if(Tree[rson].Mx >= v) res = query(rson,st,ed,v);
else if(Tree[lson].Mx >= v) res = query(lson,st,ed,v);
return res;
}
int mid = (l+r)>>1;
if(ed>mid && Tree[rson].Mx >= v) res = query(rson,st,ed,v);
if(res) return res;
if(st<=mid && Tree[lson].Mx >= v) res = query(lson,st,ed,v);
return res;
}
}T1,T2;
char s[A][A];
int dp[3][A][A];
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1 ;i<=n ;i++){
scanf("%s",s[i]+1);
}
for(int i=n ;i>=1 ;i--){
for(int j=1 ;j<=m ;j++){
if(s[i][j] != 'X') continue;
dp[0][i][j] = dp[0][i+1][j-1] + 1;
dp[1][i][j] = dp[1][i+1][j] + 1;
dp[2][i][j] = dp[2][i+1][j+1] + 1;
}
}
int ans = 0;
for(int i=1 ;i<=n ;i++){
T1.build_Tree(1,1,m);
T2.build_Tree(1,1,m);
for(int k=1 ;k<=m ;k++){
if(k&1) T1.update(1,k,min(dp[0][i][k],dp[1][i][k]) - k - 1);
else T2.update(1,k,min(dp[0][i][k],dp[1][i][k]) - k - 1);
}
for(int j=1 ;j<=m ;j++){
if(s[i][j] != 'X') continue;
int pos = 0,x = min(dp[1][i][j],dp[2][i][j]);
if(x <= ans) continue;
if(j&1) pos = T1.query(1,j,x+j-1,-j);
else pos = T2.query(1,j,x+j-1,-j);
if(pos) ans = max(ans,pos-j+1);
}
}
printf("%d\n",ans);
return 0;
}
维护方式 2 : 1116 m s 2:1116ms 2:1116ms
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
#define lson rt<<1
#define rson rt<<1|1
const int A = 3e3 + 10;
class TNode{
public:
int l,r,sum;
};
class P{
public:
int id,val;
bool operator<(const P &rhs) const{
return val < rhs.val;
}
}a[A];
class Seg_Tree{
public:
TNode Tree[A<<3];
void push_up(int rt){
Tree[rt].sum = Tree[lson].sum + Tree[rson].sum;
}
void build_Tree(int rt,int l,int r){
Tree[rt].l = l;
Tree[rt].r = r;
Tree[rt].sum = 0;
if(l == r) return;
int mid = (l+r)>>1;
build_Tree(lson,l,mid);
build_Tree(rson,mid+1,r);
push_up(rt);
}
void update(int rt,int pos,int add){
int l = Tree[rt].l,r = Tree[rt].r;
if(l==r){
Tree[rt].sum = add;
return;
}
int mid = (l+r)>>1;
if(pos<=mid) update(lson,pos,add);
else update(rson,pos,add);
push_up(rt);
}
int query(int rt,int st,int ed){
if(st>ed) return 0;
if(Tree[rt].sum == 0) return 0;
int l = Tree[rt].l,r = Tree[rt].r;
if(l == r) return l;
if(st<=l && r<=ed){
if(Tree[rson].sum > 0) return query(rson,st,ed);
return query(lson,st,ed);
}
int mid = (l+r)>>1;
int ans = 0;
if(ed>mid) ans = query(rson,st,ed);
if(ans) return ans;
if(st<=mid) ans = query(lson,st,ed);
return ans;
}
}T1,T2;
char s[A][A];
int dp[3][A][A];
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1 ;i<=n ;i++){
scanf("%s",s[i]+1);
}
for(int i=n ;i>=1 ;i--){
for(int j=1 ;j<=m ;j++){
if(s[i][j] != 'X') continue;
dp[0][i][j] = dp[0][i+1][j-1] + 1;
dp[1][i][j] = dp[1][i+1][j] + 1;
dp[2][i][j] = dp[2][i+1][j+1] + 1;
}
}
int ans = 0;
for(int i=1 ;i<=n ;i++){
T1.build_Tree(1,1,m);
T2.build_Tree(1,1,m);
for(int k=1 ;k<=m ;k++){
a[k].id = k;
a[k].val = k - min(dp[0][i][k],dp[1][i][k]) + 1;
}
sort(a+1,a+1+m);
int tot = 1;
for(int j=1 ;j<=m ;j++){
if(s[i][j] != 'X') continue;
while(tot <= m && j>=a[tot].val){
int k = a[tot++].id;
if(k&1) T1.update(1,k,1);
else T2.update(1,k,1);
}
int pos;
if(j&1) pos = T1.query(1,j,min(dp[1][i][j],dp[2][i][j])+j-1);
else pos = T2.query(1,j,min(dp[1][i][j],dp[2][i][j])+j-1);
if(pos) ans = max(ans,pos-j+1);
}
}
printf("%d\n",ans);
return 0;
}