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
维护方式2:1116ms
题意:
给定一个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; }
相关文章推荐
- Wannafly挑战赛4-dfs序&线段树|搜索&思维|BIT-树的距离
- Wannafly挑战赛3-C.位数差(思维)
- Wannafly挑战赛6 D-锁 思维
- Wannafly挑战赛6 C-逆序对【思维&组合数学】
- wannafly挑战赛4 C-割草机 思维
- Wannafly挑战赛5 A珂朵莉与宇宙(思维)
- Wannafly挑战赛6 A(二分)B(dfs)C(公式推导)D(思维)E(技巧)
- Wannafly挑战赛2 B.Travel【思维+SPFA】
- Wannafly挑战赛2 B 题 Travel 【最短路 + 思维】
- 【Wannafly挑战赛5】 A 【思维枚举】B C【排列组合 公式】
- Wannafly挑战赛6 E 双拆分数【思维】
- Wannafly挑战赛1:B-Xorto(前缀+思维)
- Wannafly挑战赛5 -- A(前缀和) B(思维)
- Wannafly挑战赛6-E:双拆分数(思维)
- Wannafly挑战赛6 - C 逆序对 (思维)
- Wannafly 挑战赛5 A 题 珂朵莉与宇宙 【思维 + 前缀和】
- 【Wannafly挑战赛9】 A【筛法 暴力】B【KMP+思维】 C【HASH】
- Wannafly挑战赛9 C 列一列 (思维取模+同步关闭)
- Wannafly挑战赛10-D-小H的询问(线段树区间合并)
- 【Wannafly挑战赛1 】B Xorto 【思维+二分】