传送门:bzoj5217
为方便表示,首先设下标从 0 0 0开始,坐标范围 0 ≤ x < n , 0 ≤ y < m 0\leq x<n,0\leq y<m 0≤x<n,0≤y<m。
可以把环境图(只考虑海水和礁石)一行一行接起来转换成一个长度为 n × m n\times m n×m的 01 01 01串 S S S(若下标从 0 0 0开始,则 S i × m + j = 1 S_{i\times m+j}=1 Si×m+j=1表示位置 ( i , j ) (i,j) (i,j)上有礁石。
同样把包围舰队的最小矩形抠出来,保留最上一行到右下角的位置的状态转换成 01 01 01串 T T T(设矩形上下边界分别为 x 1 , x 2 x_1,x_2 x1,x2,左右边界分别为 y 1 , y 2 y_1,y_2 y1,y2,则表示从 ( x 1 , 0 ) (x_1,0) (x1,0)到 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)的所有点的状态,串长 ( x 2 − x 1 ) × m + y 2 − y 1 + 1 (x_2-x_1)\times m+y_2-y_1+1 (x2−x1)×m+y2−y1+1, T ( i − x 0 ) × m + j − y 0 = 1 T_{(i-x_0)\times m+j-y_0}=1 T(i−x0)×m+j−y0=1表示位置 ( i , j ) (i,j) (i,j)上有舰。
考虑到当且仅当 S S S中长度为 ∣ T ∣ |T| ∣T∣的串(设右下角的点为 e d ed ed)不存在 S e d − i = T ∣ T ∣ − i = 1 S_{ed-i}=T_{|T|-i}=1 Sed−i=T∣T∣−i=1的情况时,舰队右下角可以处于 e d ed ed(能否到达可以 b f s bfs bfs求出,这里先求出能否处于的位置),那么将 T T T串翻转,变成 S e d − i = T i = 1 S_{ed-i}=T_i=1 Sed−i=Ti=1就成了卷积形式,做一遍 F F T FFT FFT即可。
卷积过后,若某一点位置上为 0 0 0,则表示可以处于,从舰队初始右下角的点开始 b f s bfs bfs,得到所有可以到达的点,转换成 01 01 01串 A A A( A i × m + j = 1 A_{i\times m+j}=1 Ai×m+j=1表示舰队可以处于位置 ( i , j ) (i,j) (i,j))。
每个点第一次被到达才产生贡献,所以会算重。考虑一个点 p = e d − i p=ed-i p=ed−i,若存在 A p + i = 1 A_{p+i}=1 Ap+i=1,所以同样转成了卷积形式 A p + i = T e d − i = 1 A_{p+i}=T_{ed-i}=1 Ap+i=Ted−i=1。再 F F T FFT FFT后统计系数大于 0 0 0的个数即可(实际上对应的是位数 − ( ∣ T ∣ − 1 ) -(|T|-1) −(∣T∣−1)上的点是否被到达)。
用myyfft似乎要快一些myyfft戳这里
#include
#define RI register
using namespace std;
typedef double db;
const int N=2e6+10,M=702;
const int MX=M*M;
const db pi=acos(-1.0);
int ans,n,m,all,are,len,L,rv[N],t[MX];
int xx[2],yy[2],H,W,g[MX],vs[MX];
char s[M][M];
struct P{
int x,y;
P(){};
P(int x_,int y_){x=x_;y=y_;}
}temp;
queue<P>que;
struct cc{
db r,i;
cc(){r=i=0;};
cc(db r_,db i_){r=r_;i=i_;}
cc operator +(const cc&ky){return cc(r+ky.r,i+ky.i);}
cc operator -(const cc&ky){return cc(r-ky.r,i-ky.i);}
cc operator *(const cc&ky){return cc(r*ky.r-i*ky.i,r*ky.i+i*ky.r);}
cc operator /(const int&ky){return cc(r/(db)ky,i/(db)ky);}
inline cc conj(){return cc(r,-i);}
}a[N],b[N];
inline void FFT(cc *e,int ptr)
{
RI int i,j,k,t;cc bs,ori,ix,iy;
for(i=1;i<len;++i) if(i<rv[i]) swap(e[i],e[rv[i]]);
for(i=1;i<len;i<<=1){
ori=cc(cos(pi/i),ptr*sin(pi/i));
for(j=0;j<len;j+=(i<<1)){
bs=cc(1,0);
for(k=0;k<i;++k,bs=bs*ori){
ix=e[j+k];iy=e[i+j+k]*bs;
e[j+k]=ix+iy;e[i+j+k]=ix-iy;
}
}
}
if(ptr==1) return;
for(i=0;i<len;++i) e[i]=e[i]/len;
}
inline void init()
{
RI int i,j,pos=0;
scanf("%d%d",&n,&m);
all=n*m;
xx[0]=n+1;yy[0]=m+1;
for(i=0;i<n;++i){
scanf("%s",s[i]);
for(j=0;j<m;++j){
if(s[i][j]=='#') a[pos].r=1;
else if(s[i][j]=='o'){
xx[0]=min(i,xx[0]);
xx[1]=max(i,xx[1]);
yy[0]=min(j,yy[0]);
yy[1]=max(j,yy[1]);
}
pos++;
}
}
H=xx[1]-xx[0]+1;W=yy[1]-yy[0]+1;
pos=0;are=(H-1)*m+W;
for(i=xx[0];i<=xx[1];++i,pos+=m)
for(j=yy[0];j<=yy[1];++j)
if(s[i][j]=='o')
a[are-1-pos-j+yy[0]].i=1,
t[pos+j-yy[0]]=1;
}
inline void ins(P p)
{
int rc=p.x*m+p.y;
if(!g[rc] || vs[rc]) return;
vs[rc]=1;
if(p.x>0) que.push(P(p.x-1,p.y));
if(p.x<n-1) que.push(P(p.x+1,p.y));
if(p.y>0) que.push(P(p.x,p.y-1));
if(p.y<m-1) que.push(P(p.x,p.y+1));
}
inline void work()
{
RI int i,j;
for(len=1,L=0;len<all+are;len<<=1) L++;
for(i=1;i<len;++i) rv[i]=(rv[i>>1]>>1)|((i&1)<<(L-1));
FFT(a,1);
for(i=0;i<len;++i){
j=(len-i)&(len-1);
b[i]=(a[i]*a[i]-(a[j]*a[j]).conj())*cc(0,-0.25);
}
FFT(b,-1);
for(i=H-1;i<n;++i)
for(j=W-1;j<m;++j)
if(b[i*m+j].r<0.5) g[i*m+j]=1;
que.push(P(xx[1],yy[1]));
for(;!que.empty();){
temp=que.front();que.pop();
ins(temp);
}
for(i=0;i<len;++i) a[i]=cc(0,0);
for(i=0;i<all;++i) if(vs[i]) a[i].r=1;
for(i=0;i<are;++i) if(t[i]) a[i].i=1;
FFT(a,1);
for(i=0;i<len;++i){
j=(len-i)&(len-1);
b[i]=(a[i]*a[i]-(a[j]*a[j]).conj())*cc(0,-0.25);
}
FFT(b,-1);
for(i=0;i<len;++i) if(b[i].r>0.5) ans++;
printf("%d\n",ans);
}
int main(){
init();
work();
return 0;
}