Wannafly挑战赛2 C 思维 + 线段树

题目链接


题意:

给定一个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 kj+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])>=kj+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])>=kj+1
3 ) . k > = j 3).k>=j 3).k>=j

移项后得到:
1 ) . j &lt; = k &lt; = m i n ( d p [ 1 ] [ i ] [ j ] , d p [ 2 ] [ i ] [ j ] ) + j − 1 1). j &lt;= k &lt;= min(dp[1][i][j],dp[2][i][j]) + j - 1 1).j<=k<=min(dp[1][i][j],dp[2][i][j])+j1
2 ) . m i n ( d p [ 0 ] [ i ] [ k ] , d p [ 1 ] [ i ] [ k ] ) − k − 1 &gt; = − j 2).min(dp[0][i][k],dp[1][i][k]) - k - 1 &gt;= -j 2).min(dp[0][i][k],dp[1][i][k])k1>=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])k1。故题目便转化成了在区间 ( 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])+j1)中找到最大的下标使其维护的值 &gt; = − j &gt;=-j >=j

因为蝴蝶形状有中心点,故长度 k − j + 1 k-j+1 kj+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 kmin(dp[0][i][k],dp[1][i][k])+1排序,然后从左到右枚举 j j j时,一点点把 k k k加入考虑的范畴。
稍微改一改线段树的维护方式也可以搞出来。

代码:
维护方式 1 : 658 m s 1:658ms 1658ms

#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 21116ms

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值