UVA 12633 Super Rooks on Chessboard(FFT)
题意:
给你一个R*C的棋盘,棋盘上的棋子会攻击,一个棋子会覆盖它所在的行,它所在的列,和它所在的从左上到右下的对角线,那么问这个棋盘上没有被覆盖的棋盘格子数。数据范围R,C,N<=50000
思路:
直接做肯定会超时,所以需要一种\(nlogn\)的算法。我们一步一步来。
首先,我们肯定需要给被覆盖的行被覆盖的列做上标记,visx标记被覆盖的行,visy标记被覆盖的列,visd标记被覆盖的对角线
那么就是 visx[r]=1,visy[c]=1,visd[r-c+C]=1,给对角线这么标号避免了负的下标,即从右上角开始到左下角从1标到R-1+C,而且下面还会用到。
然后,如果我们不考虑被覆盖的对角线,那么没有被覆盖的格子数就是未被覆盖的行数\(*\)未被覆盖的列数,但是还有被覆盖的对角线。
下面,我们设为行的覆盖情况的集合为{r1,r2,r3,ri,rR}其中下标代表行号,值为1或0,代表是否被覆盖,1是没被覆盖,0是被覆盖,列也一样{c1,c2,c3,cj,cC},我们再设对角线的{d1,d2,d3,dk,dx+y-1}但这里的值是这个对角线未被覆盖的值,
然后我们看,一开始我们没有考虑对角线,现在我们就要把对角线覆盖的部分从行列未覆盖的部分减去,那么我们就要知道在未考虑对角线覆盖情况下每个对角线没有被覆盖的格子数,然后根据visd把未被行列覆盖但被对角线覆盖的格子数减去。
那么每个对角线,那么标号为k的对角线的未被覆盖格子数是什么
\[\sum\sum(r_i\times c_j)(i-j+C=k)=d_k$$。
那么我们就发现了,$ri\times cj$就相当于系数相乘,下标i-j+C就相当于指数相加,那么这时候就可以把它转化为多项式了,相当于两个多项式相乘,那么就要用到FFT了。
下面就是多项式系数赋值了。首先行的多项式就这么顺序赋值即可,又因为指数相加结果是i-j+C,那么行的指数就是i,列的指数就是拿j的系数做指数C-j的系数,这样相加就可以得到上面的求和公式的效果了。套FFT模板搞一下就行了,最后再结合visd去重即可。
```cpp
# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <complex>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
const double pi=acos(-1.0);
# define eps 1e-8
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FDR(i,a,n) for(int i=a; i>=n; --i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
inline int Scan() {
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
inline void Out(int a) {
if(a<0) {putchar('-'); a=-a;}
if(a>=10) Out(a/10);
putchar(a%10+'0');
}
const int N=50005;
//Code begin....
typedef complex<double> cmx;
int a[N<<2], b[N<<2];
cmx x[N<<2], y[N<<2];
bool visr[N<<2], visc[N<<2], visd[N<<2];
struct Node{int x, y;}node[N];
void change(cmx x[], int len) {
int i, j, k;
for(i=1, j=len>>1; i<len-1; ++i) {
if(i<j) swap(x[i],x[j]);
k=len>>1;
while(j>=k) j-=k, k>>=1;
if(j<k) j+=k;
}
}
void fft(cmx x[], int len, int on) {
change(x,len);
for(int i=2; i<=len; i<<=1) {
cmx wn(cos(-on*2*pi/i),sin(-on*2*pi/i));
for(int j=0; j<len; j+=i) {
cmx w(1,0);
FOR(k,j,j+i/2-1) {
cmx u=x[k], v=x[k+i/2]*w;
x[k]=u+v; x[k+i/2]=u-v; w*=wn;
}
}
}
if(on==-1) FOR(i,0,len-1) x[i]/=len;
}
int main()
{
int T, r, c, R, C, n;
scanf("%d",&T);
FOR(cas,1,T) {
mem(visr,0); mem(visc,0); mem(visd,0); mem(a,0); mem(b,0); r=0; c=0;
LL ans=0;
scanf("%d%d%d",&R,&C,&n);
FOR(i,1,n) scanf("%d%d",&node[i].x,&node[i].y), visr[node[i].x]=visc[node[i].y]=visd[node[i].x-node[i].y+C]=true;
FOR(i,1,R) if (!visr[i]) a[i]=1, ++r;
FOR(i,1,C) if (!visc[i]) b[C-i]=1, ++c;
ans=(LL)r*c;
int len=1;
while (len<=R+C) len<<=1;
FOR(i,0,len-1) x[i]=cmx(a[i],0), y[i]=cmx(b[i],0);
fft(x,len,1); fft(y,len,1);
FOR(i,0,len-1) x[i]=x[i]*y[i];
fft(x,len,-1);
FOR(i,0,len-1) if (visd[i]) ans-=(int)(x[i].real()+0.5);
printf("Case %d: %lld\n",cas,ans);
}
return 0;
}
```\]