Super Rooks on Chessboard,UVA - 12633,FFT

Super Rooks on Chessboard,UVA - 12633

https://vjudge.net/problem/UVA-12633/origin
Let’s assume there is a new chess piece named Super-rook. When placed at a cell of a chessboard, it
attacks all the cells that belong to the same row or same column. Additionally it attacks all the
cells of the diagonal that goes from top-left to bottom-right direction through that cell.
N Super-rooks are placed on a R × C chessboard. The rows are numbered 1 to R from top to
bottom and columns are numbered 1 to C from left to right of the chessboard. You have to find the
number of cells of the chessboard which are not attacked by any of the Super-rooks.
The picture on the left shows the attacked cells when a Super-rook is placed at cell (5, 3) of a 6 × 6
chessboard. And the picture on the right shows the attacked cells when three Super-rooks are placed
at cells (3, 4), (5, 3) and (5, 6). These pictures (Left and right one) corresponds to the first and second
sample input respectively.
思路: 首先很容易求出只覆盖横竖时总共覆盖的格子数,然后进行斜线覆盖数的计算,如果一个格子的坐标为(a,b),那么它覆盖的斜线方程为x +y = a+b(将左下角看作(0,0)),即会将这条斜线上的所有格子覆盖,首先加上这条斜线上的格子数,然后因为横竖已经覆盖了一些格子,需要先分别减去这条斜线上横竖覆盖了的格子数,然后还存在一种情况就是横竖覆盖了这条斜线上的同一个格子,就多减了一次,需要再加上,即需要知道有多少横竖的x +y = a+b,可以一开始记录下哪些横被覆盖,哪些竖被覆盖,然后做下卷积,a+b的系数即为对应结果

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ri register int
#define MAXN 50005
#define ll long long
using namespace std;

const double pi=acos(-1.0);
struct cpx
{
    double r, i;
    inline void operator +=(const cpx &b){ r+=b.r, i+=b.i;}
    inline cpx operator +(const cpx &b)const{ return (cpx){r+b.r, i+b.i};}
    inline cpx operator -(const cpx &b)const{ return (cpx){r-b.r, i-b.i};}
    inline cpx operator *(const cpx &b)const{ return (cpx){r*b.r-i*b.i, r*b.i+i*b.r};}
    inline cpx operator *(const double b)const{ return (cpx){r*b, i*b};}
    inline cpx operator ~()const{return (cpx){r, -i};}
}a[MAXN<<2],b[MAXN << 2],w[MAXN<<2];

inline void DFT_(cpx *f, int n)
{
    for(ri i=0, j=0; i<n; ++i)
    {
        if(i>j) swap(f[i], f[j]);
        for(ri k=n>>1; (j^=k)<k; k>>=1);
    }
    for(ri i=1; i<n; i<<=1) for(ri j=0; j<n; j+=i<<1)
            for(ri k=j; k<j+i; ++k)
            {
                cpx t=w[i+k-j]*f[k+i];
                f[k+i]=f[k]-t, f[k]+=t;
            }
}
inline void DFT(cpx *f, int n)
{
    if(n==1) return;
    n>>=1;
    static cpx a[MAXN<<1];
    for(ri i=0; i<n; ++i) a[i]=(cpx){f[i<<1].r, f[i<<1|1].r};
    DFT_(a, n);
    for(ri i=0; i<n; ++i)
    {
        cpx q=~a[(n-i)&(n-1)], x=(a[i]+q)*0.5, y=(a[i]-q)*(cpx){0, -0.5}, t=y*w[n+i];
        f[i]=x+t, f[n+i]=x-t;
    }
}
inline void IDFT(cpx *f, int n)
{
    if(n==1) return;
    reverse(f+1, f+n), n>>=1;
    static cpx a[MAXN<<1];
    for(ri i=0; i<n; ++i)
        a[i]=(f[i]+f[i+n])*0.5 + (f[i]-f[i+n])*(cpx){0, 0.5}*w[n+i];
    DFT_(a, n);
    double k=1.0/n;
    for(ri i=0; i<n; ++i) f[i<<1]=(cpx){a[i].r*k, 0}, f[i<<1|1]=(cpx){a[i].i*k, 0};
}

int n,m,k;
int visr[MAXN],visc[MAXN],visd[MAXN<<1];
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas = 1;cas <= t;++cas)
    {
        scanf("%d%d%d",&n,&m,&k);
        int x,y;
        memset(visr,0,sizeof(visr));
        memset(visc,0,sizeof(visc));
        memset(visd,0,sizeof(visd));
        while(k--)
        {
            scanf("%d%d",&x,&y);
            visr[n+1-x] = visc[y] = visd[n+1-x+y] = 1;
        }

        n,m;
        int len = 1;
        while(len < n+m+2) len<<=1;
        for(int i = 0;i <= n;i++)
            a[i] = (cpx){(double)visr[i],0};
        for(int i = n+1;i < len;i++)
            a[i] = (cpx){0,0};
        for(int i = 0;i <= m;i++)
            b[i] = (cpx){(double)visc[i],0};
        for(int i = m+1;i < len;i++)
            b[i] = (cpx){0,0};
        for(ri i=1; i<len; i<<=1)
        {
            w[i]=(cpx){1, 0};
            for(ri j=1; j<i; ++j)
                w[i+j]=((j&31)==1?(cpx){cos(pi*j/i), sin(pi*j/i)}:w[i+j-1]*w[i+1]);
        }
        DFT(a,len),DFT(b,len);
        for(int i = 0;i < len;++i)
            a[i] = a[i] * b[i];
        IDFT(a,len);

        ll ans = 0,sumr = 0;
        for(int i = 1;i <= n;++i)
            if(visr[i])
                ++sumr;
        ans = sumr * m;
        for(int i = 1;i <= m;++i)
            if(visc[i])
                ans += n-sumr;
        for(int i = 2;i <= n;++i)
            visr[i] = visr[i-1] + visr[i];
        for(int i = 2;i <= m;++i)
            visc[i] = visc[i-1] + visc[i];
        for(int i = 2;i <= n+m;++i)
        {
            if(visd[i])
            {
                int x1,y1,x2,y2;
                if(i-1 <= n)
                    x2 = i-1,y1 = 1;
                else
                    x2 = n,y1 = i-n;
                if(i-1 <= m)
                    y2 = i-1,x1 = 1;
                else
                    y2 = m,x1 = i-m;
                ans += max(x2-x1+1,y2-y1+1) - visr[x2] + visr[x1-1] - visc[y2] + visc[y1-1] + (ll)(a[i].r+0.5);
            }
        }
        printf("Case %d: %lld\n",cas,1ll*n*m-ans);
    }
    return 0;
}
/*
//
1
4 5 4
2 3
4 3
3 4
2 5
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值