您的位置:首页 > 其它

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

2017-10-30 20:04 441 查看
题目链接

题意:

给定一个n*m的矩阵,矩阵元素由X和O构成,请求出其中最大的由X构成的蝴蝶形状。

由X构成的蝴蝶形状的定义如下:

存在一个中心点,并且其往左上、左下、右上、右下四个方向扩展相同的长度(扩展的长度上都是X),且左上顶点与左下顶点、右上顶点与右下顶点之间的格子全由X填充。我们不在意在蝴蝶形状内部是X还是O。

例如:

XOOOX

XXOXX

XOXOX

XXOXX

XOOOX

是一个X构成的蝴蝶形状。

X

也是。



XOOX

OXXO

OXXO

XOXX

不是(不存在中心点)。

输出:一行一个整数表示最大的由X构成的蝴蝶形状的对角线的长度。

思路:

很巧妙的题目转化方式!

首先O(nm)预处理三个量:

dp[0][i][j]:从(i,j)的位置开始左下方向连续的X的个数。

dp[1][i][j]:从(i,j)的位置开始向下连续的X的个数。

dp[2][i][j]:从(i,j)的位置开始右下方向连续的X的个数。

考虑枚举每一个蝴蝶形状的左上角顶点位置(i,j)。

则我们需要找到一个最大的k,使(i,k)为一个蝴蝶形状的右上顶点位置。

当蝴蝶形状合法时,k需要满足的条件为:(蝴蝶形状的长为k−j+1)

1).min(dp[1][i][j],dp[2][i][j])>=k−j+1

2).min(dp[0][i][k],dp[1][i][k])>=k−j+1

3).k>=j

移项后得到:

1).j<=k<=min(dp[1][i][j],dp[2][i][j])+j−1

2).min(dp[0][i][k],dp[1][i][k])−k−1>=−j

因为需要最大化k,故可以考虑用线段树维护,对于每一个k,线段树的相应节点保存min(dp[0][i][k],dp[1][i][k])−k−1。故题目便转化成了在区间(j,min(dp[1][i][j],dp[2][i][j])+j−1)中找到最大的下标使其维护的值>=−j。

因为蝴蝶形状有中心点,故长度k−j+1一定为奇数,需要两个线段树,对于奇偶性不同的k分开维护。

另外Wannafly群还提供了另外一个不错的维护方式。

将k按照k−min(dp[0][i][k],dp[1][i][k])+1排序,然后从左到右枚举j时,一点点把k加入考虑的范畴。

稍微改一改线段树的维护方式也可以搞出来。

代码:

维护方式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: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息