UVA 12633 Super Rooks on Chessboard ——FFT
发现对角线上的和是一个定值。
然后就不考虑斜着,可以处理出那些行和列是可以放置的。
然后FFT,统计出每一个可行的项的系数和就可以了。
#include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define ll long long #define mp make_pair #define maxn 200005 struct Complex{ double x,y; Complex () {} Complex (double _x,double _y) {x=_x;y=_y;} Complex operator + (Complex a) {return Complex(x+a.x,y+a.y);} Complex operator - (Complex a) {return Complex(x-a.x,y-a.y);} Complex operator * (Complex a) {return Complex(x*a.x-y*a.y,x*a.y+y*a.x);} }A[maxn],B[maxn],C[maxn]; int t,r,c,n,k,m,len,line[maxn],row[maxn],ang[maxn],rev[maxn],cntl=0,cntr=0,kas=0; ll ans=0; const double pi=acos(-1.0); void FFT(Complex * x,int n,int flag) { F(i,0,n-1) if (rev[i]>i) swap(x[i],x[rev[i]]); for (int m=2;m<=n;m<<=1) { Complex wn=Complex(cos(2*pi/m),flag*sin(2*pi/m)); for (int i=0;i<n;i+=m) { Complex w=Complex(1.0,0); for (int j=0;j<(m>>1);++j) { Complex u=x[i+j],v=x[i+j+(m>>1)]*w; x[i+j]=u+v; x[i+j+(m>>1)]=u-v; w=w*wn; } } } if (flag==-1) F(i,0,n-1) x[i].x=(x[i].x+0.3)/n; } int main() { scanf("%d",&t); while (t--) { memset(line,0,sizeof line); memset(row,0,sizeof row); memset(ang,0,sizeof ang); cntl=cntr=0; scanf("%d%d%d",&r,&c,&k); F(i,1,k) { int x,y; scanf("%d%d",&x,&y); x=r-x+1; if (!line[x]) cntl++; if (!row[y]) cntr++; line[x]=1; row[y]=1; ang[x+y]=1; } ans=((ll)r-(ll)cntl)*((ll)c-(ll)cntr); n=r+c+10;m=1;len=0; while (m<=n) m<<=1,len++;n=m; F(i,0,n-1) { int ret=0,t=i; F(j,1,len) ret<<=1,ret|=t&1,t>>=1; rev[i]=ret; } F(i,0,n-1) A[i].x=B[i].x=A[i].y=B[i].y=0; F(i,1,r) if (!line[i]) A[i].x=1.0; F(i,1,c) if (!row[i]) B[i].x=1.0; FFT(A,n,1); FFT(B,n,1); F(i,0,n-1) C[i]=A[i]*B[i]; FFT(C,n,-1); F(i,1,r+c) if (ang[i]) ans-=(ll)C[i].x; printf("Case %d: ",++kas);printf("%lld\n",ans); } }