好像是被我水过了 因为题目给了5s
题意:问图像中到底有多少个z型图像。
思路:对于每一个点都有可能是最左下角的点,那么把对于每一个 斜边 看成大区间 区间每一个点都是都有两个值 一个是能到右边的最远距离 一个是到左边的最远距离+i(加权)
只要对于每一个点的答案是把右边的最远距离作为区间长度 统计出区间 左边是否能满足
比如 3x3
zzz
.z.
zzz
对于 最左下角的点 统计区间长度为3 加权距离为行数+左边的最远距离 为4 然后区间内 只要大于4的数 就是满足条件的
就是转换成问这个数在区间内 是第几大的问题 就抄了别人模版=。=
ps:这个方法4s多才过,虽然我代码写搓 也有部分原因- -,不过 他们都是1s内过的 惭愧
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<queue>
#include<math.h>
using namespace std;
#define LL long long
#define MOD 1000000007
int a[3005][3005];
int c[3005][3005];
int d[3005][3005];
char ch[3005][3005];
#define CLR(a) memset(a,0,sizeof(a))
const int MAXN = 3010;
struct Node
{
int l,r;
int len(){return r-l;}
int mid(){return (l+r)/2;}
bool in(int ll,int rr){return l>=ll && r<=rr;}
void lr(int ll,int rr){l = ll; r=rr;}
}node[MAXN*4];
int num_left[15][MAXN],seg[15][MAXN],sa[MAXN];
void Init()
{
//CLR(seg),CLR(num_left),CLR(node);
memset(num_left,0,sizeof(num_left));
memset(node,0,sizeof(node));
}
void PaBuild(int t,int l, int r,int d)
{
node[t].lr(l,r);
if(node[t].len() == 0)return;
int mid=(l+r)/2,lsame=mid-l+1;
for(int i=l;i<=r;i++)//首先确定分到每一侧的数的数目
if(seg[d][i] < sa[mid])//因为相同的元素可能被分到两侧
lsame--;
int lpos=l,rpos=mid+1;
for(int i=l;i<=r;i++)
{
if(i == l)
num_left[d][i]=0;
else
num_left[d][i]=num_left[d][i-1];
if(seg[d][i] < sa[mid])
{
num_left[d][i]++;
seg[d+1][lpos++]=seg[d][i];
}
if(seg[d][i] > sa[mid])
seg[d+1][rpos++] = seg[d][i];
if(seg[d][i] == sa[mid])
if(lsame > 0)// 如果大于0,说明左侧可以分和sa[mid]相同的数字
{
lsame--;
num_left[d][i]++;
seg[d+1][lpos++]=seg[d][i];
}
else //反之,说明左侧数字已经分满了,就分到右边去
seg[d+1][rpos++]=seg[d][i];
}
PaBuild(t*2,l,mid,d+1);
PaBuild(t*2+1,mid+1,r,d+1);
}
void Build(int s,int t)
{
sort(sa+s,sa+t+s);
PaBuild(1,s,t,1);
}
int find_rank(int t,int l,int r,int d,int val)//val指的是k
{
if(node[t].len() == 0)return seg[d][l];
int s,ss; //s表示区间[l,r]有多少个小于sa[mid]的数被分到左边
if( l == node[t].l)//ss表示从当前区间的L到l-1有多少个小于sa[mid]的数被分到左边,L,R指的是树上当前节点的区间范围
ss=0;
else
ss=num_left[d][l-1];
s=num_left[d][r]-ss;
if(s>=val)
return find_rank(t*2, node[t].l+ss,node[t].l+ss+s-1,d+1,val);
else
{
int mid = node[t].mid();
int bb=l-node[t].l-ss; //表示从当前区间L到l-1有多少个分到右边
int b=r-l+1-s; //表示[l,r]有多少个分到右边
return find_rank(t*2+1,mid+bb+1,mid+bb+b,d+1,val-s);
}
}
int Query(int s,int t,int k)
{
return find_rank(1,s,t,1,k);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",ch[i]+1);
n=max(n,m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(ch[i][j]=='z')
a[i][j]=1;
}
for(int i=1;i<=n;i++)
{
int sum=0;
for(int j=n;j>=1;j--)
{
if(a[i][j])
sum++;
else
sum=0;
c[i][j]=sum;
}
}
for(int i=1;i<=n;i++)
{
int sum=0;
for(int j=1;j<=n;j++)
{
if(a[i][j])
sum++;
else
sum=0;
d[i][j]=sum;
}
}
LL ans=0;
for(int i=1;i<2*n;i++)
{
int len=1;
Init();
for(int j=1;j<=n;j++,len++)
{
int x=i-j+1,y=j;
if(i-j+1<=0)break;
if(i-j+1>n){
sa[len]=seg[1][len]=0;
continue;
}
sa[len]=seg[1][len]=d[y][x]+j;
}
Build(1,len-1);
int h=1;
for(int j=1;j<=n;j++)
{
if(i-j+1<=0)break;
if(i-j+1>n)continue;
int x=i-j+1,y=j;
if(d[y][x]==0){h=j+1;continue;}
int t=max(j-c[y][x]+1,h);
int l=1,r=j-t+1;
int ret=r;
while(l<=r)
{
int mid=(l+r)/2;
int q=Query(t,j,mid);
if(q>=j+1)
{
r=mid-1;
ret=mid;
}
else
{
l=mid+1;
}
}
ans+=j-t+1-ret+1;
}
}
printf("%lld\n",ans);
return 0;
}