您的位置:首页 > 其它

codeforces628 E. Zbazi in Zeydabad 选择枚举对象+用顺序减少重复计算+树状数组优化

2016-02-25 09:37 323 查看
问一个n*m的网格中有多少个z

基本想法应该是枚举z,我想的是枚举中间的z,但是好像复杂度太高了,题解的方法是枚举右上角的z,然后预处理出每个点往左最多的和往左下最多的(nm时间)

枚举每个xy,判断以他为右上角的z有多少个,因为对角线可以变短一点,所以要枚举每个对角线上的点,看右边最长能不能到y

至此算法复杂度为o(nmm)

想办法优化,枚举每一个显然不怎么好优化……只有计算对角线上有哪些能到这个y能想办法优化,需要知道这条对角线上有多少点能到y,就是求有多少点加上往右的最大值之后能>y,找到重复计算的部分,同一个对角线上,y+最大值可能很大,但是每次都要算一次,浪费了很多时间,所以要从右边最大的算起,每条对角线上的某个位置,如果以前可以,那么更小的就也可以所以下次就不用再比较,可是这样复杂度依然没有变……需要一个快速求出全部和的方法

就是用树状数组(或者线段树)每次算刚好能到j列的那些,每个对角线保存一个树状数组,就可以利用前面的性质依次计算

好难啊……自己想不出来?

下面是AC代码,初始化的部分有点麻烦了……不过居然AC也是不容易

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
int zl[3001][3001];
int zr[3001][3001];
int zd[3001][3001];
int bit[6005][3005];
int n,m;
int gra[3001][3001];
struct node{
int x;
int y;
node(int a,int b):x(a),y(b){};
};
vector<node> value[3005];//加了之后为j的

int sum(int k,int i){
int s = 0;
while(i > 0){
s += bit[k][i];
i -= i & -i;
}
return s;
}

void add(int j,int i,int x){
while(i <= m){
bit[j][i] += x;
i += i & -i;
}
}

void init()
{
for(int i = 1; i <= n; i++){//直接加后面一个就行了,我这样写烦了
int tem = 0;
for(int j = m; j >= 1; j--){
if (gra[i][j] == 'z'){
tem++;
zr[i][j] = tem;
}
else tem = 0;
}
}
for(int i = 1; i <= n; i++){
int tem = 0;
for(int j = 1; j <= m; j++){
if (gra[i][j] == 'z'){
tem++;
zl[i][j] = tem;
}
else tem = 0;
}
}
for(int i = 1; i <= n; i++){
int tem = 0;
for(int k = 0; i - k >= 1 && 1 + k <= m; k++){
if (gra[i - k][1 + k] == 'z'){
tem++;
zd[i - k][1 + k] = tem;
}
else tem = 0;
}
}
for(int j = 2; j <= m; j++){
int tem = 0;
for(int k = 0; (n - k) >= 1 && j + k <= m; k++){
if (gra[n - k][j + k] == 'z'){
tem++;
zd[n - k][j + k] = tem;
}
else tem = 0;
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
value[j + zr[i][j] - 1].push_back(node(i,j));
}

int main()
{
long long ans = 0;
memset(bit ,0,sizeof(bit));//清空
scanf("%d %d",&n,&m);
getchar();
//输入
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++)
scanf("%c",&gra[i][j]);
getchar();
}
init();
for(int j = m; j >= 1; j--){
for(int i = 0; i < value[j].size();i++)
add(value[j][i].x + value[j][i].y,value[j][i].y,1);//
for(int i = 1; i <= n; i++){
int len = min(zl[i][j],zd[i][j]);
ans += (sum(i + j,j) - sum(i + j,j - len));
}
}
printf("%I64d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息