【题目】
原题地址
给定一个
n
×
m
n\times m
n×m的
01
01
01矩阵,其中
1
1
1不能走。给定一个人的行走路线,求所有可能的起点。
【解题思路】
我们考虑将走的路径看作
1
1
1,然后将走出的路径补
0
0
0成为一个
n
×
m
n\times m
n×m矩阵。现在的问题就转化为在原矩阵中放一个矩阵,使得两个矩阵的或为
0
0
0,求方案数。
将矩阵展开为一维后,实际上就是对应位置上的或值为
0
0
0。
由于只有
0
0
0和
1
1
1,那么或运算和乘法运算是等价的。
于是将其中一个序列反转,做
f
f
t
fft
fft,这时候卷积中第
i
i
i位就代表下标和为
i
i
i的各项乘积,实际上就表示路径矩阵在原矩阵中起始位置为
i
−
n
×
m
i-n\times m
i−n×m时,矩阵各项的乘积和。
于是现在看对应位置中有多少为为0即可。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef double db;
const db pi=acos(-1);
const int N=5e6+10,M=N<<1;
int n,m,K,mxx,mix,mxy,miy,nx,ny,lim,L,ans;
int rev[M];
char s[N];
struct cd
{
db r,i;
cd(){}
cd(db r,db i):r(r),i(i){}
cd operator + (const cd&x)const{return cd(r+x.r,i+x.i);}
cd operator - (const cd&x)const{return cd(r-x.r,i-x.i);}
cd operator * (const cd&x)const{return cd(r*x.r-i*x.i,r*x.i+i*x.r);}
}a[M],b[M];
void fft(cd *a,int n,int f)
{
for(int i=0;i<n;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
cd wn=cd(cos(pi/i),f*sin(pi/i));
for(int j=0;j<n;j+=i<<1)
{
cd w=cd(1,0);
for(int k=0;k<i;++k,w=w*wn)
{
cd x=a[j+k],y=w*a[i+j+k];
a[j+k]=x+y;a[i+j+k]=x-y;
}
}
}
if(!~f) for(int i=0;i<n;++i) a[i].r/=n;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("LOJ6388.in","r",stdin);
freopen("LOJ6388.out","w",stdout);
#endif
scanf("%d%d%d",&n,&m,&K);
for(int i=0;i<n;++i)
{
scanf("%s",s);
for(int j=0;j<m;++j) a[i*m+j].r=s[j]=='1';
}
scanf("%s",s);
for(int i=0;i<K;++i)
{
if(s[i]=='w') --nx;
else if(s[i]=='s') ++nx;
else if(s[i]=='a') --ny;
else ++ny;
mxx=max(mxx,nx);mix=min(mix,nx);
mxy=max(mxy,ny);miy=min(miy,ny);
}
nx=ny=0;mxx-=mix;mxy-=miy;
b[-mix*m-miy].r=1;
for(int i=0;i<K;++i)
{
if(s[i]=='w') --nx;
else if(s[i]=='s') ++nx;
else if(s[i]=='a') --ny;
else ++ny;
b[(nx-mix)*m+(ny-miy)].r=1;
}
//for(int i=0;i<n;++i,puts("")) for(int j=0;j<m;++j) printf("%.0lf ",b[i*m+j].r);
int t=n*m-1;
for(L=0,lim=1;lim<=2*t;) lim<<=1,++L;
for(int i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
reverse(b,b+t+1);
/*for(int i=0;i<lim;++i) printf("%0.lf ",a[i].r); puts("");
fft(a,lim,1);
for(int i=0;i<lim;++i) printf("%0.lf ",a[i].r); puts("");
fft(a,lim,-1);
for(int i=0;i<lim;++i) printf("%0.lf ",a[i].r); puts("");*/
fft(a,lim,1);fft(b,lim,1);
for(int i=0;i<lim;++i) a[i]=a[i]*b[i];
fft(a,lim,-1);
for(int i=0;i<n-mxx;++i) for(int j=0;j<m-mxy;++j)
if(a[t+i*m+j].r<0.5) ++ans;
printf("%d",ans);
return 0;
}
【总结】
一道转化模型的好题,有点类似
f
f
t
fft
fft解决字符串匹配问题。