1. 程式人生 > >【BZOJ】5217: [Lydsy2017省隊十連測]航海艦隊 -myyFFT

【BZOJ】5217: [Lydsy2017省隊十連測]航海艦隊 -myyFFT

題解

為方便表示,首先設下標從00開始,座標範圍0x<n,0y<m0\leq x<n,0\leq y<m

可以把環境圖(只考慮海水和礁石)一行一行接起來轉換成一個長度為n×mn\times m0101SS(若下標從00開始,則Si×m+j=1S_{i\times m+j}=1表示位置(i,j)(i,j)上有礁石。

同樣把包圍艦隊的最小矩形摳出來,保留最上一行到右下角的位置的狀態轉換成0101TT(設矩形上下邊界分別為x1,x2x_1,x_2,左右邊界分別為y1,y2

y_1,y_2,則表示從(x1,0)(x_1,0)(x2,y2)(x_2,y_2)的所有點的狀態,串長(x2x1)×m+y2y1+1(x_2-x_1)\times m+y_2-y_1+1T(ix0)×m+jy0=1T_{(i-x_0)\times m+j-y_0}=1表示位置(i,j)(i,j)上有艦。

考慮到當且僅當SS中長度為T|T|的串(設右下角的點為eded)不存在Sedi=TTi=1S_{ed-i}=T_{|T|-i}=1

=TTi=1的情況時,艦隊右下角可以處於eded(能否到達可以bfsbfs求出,這裡先求出能否處於的位置),那麼將TT串翻轉,變成Sedi=Ti=1S_{ed-i}=T_i=1就成了卷積形式,做一遍FFTFFT即可。

卷積過後,若某一點位置上為00,則表示可以處於,從艦隊初始右下角的點開始bfsbfs,得到所有可以到達的點,轉換成0101AA(Ai×m+j=1A_{i\times m+j}=1表示艦隊可以處於位置(i,j)(i,j))。

每個點第一次被到達才產生貢獻,所以會算重。考慮一個點p=edip=ed-i

,若存在Ap+i=1A_{p+i}=1,所以同樣轉成了卷積形式Ap+i=Tedi=1A_{p+i}=T_{ed-i}=1。再FFTFFT後統計係數大於00的個數即可(實際上對應的是位數(T1)-(|T|-1)上的點是否被到達)。

用myyfft似乎要快一些

程式碼

#include<bits/stdc++.h>
#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