UVA12633:Super Rooks on Chessboard (FFT)

题目传送门:https://cn.vjudge.net/problem/UVA-12633


题目大意:现在有一个r*c的白色棋盘,上面有n个棋子。每个棋子会将它所在行和所在列,以及所在主对角线染黑(主对角线就是左上到右下)。问最终有多少个白色格子。不超过20组数据,r,c,n<=50000。


题目分析:做了几道CF的题愉悦身心之后,我又开始刷回FFT的题了(虽然这题是水题)。

如果每个棋子只染所在行和列,那么这就是个简单的计数问题。现在我们需要知道每个棋子所在的主对角线还有多少没有染成黑色。由于同一条主对角线上的格子,横纵坐标之差为定值,不妨将没染的行,列设为1,将列翻转,然后做FFT即可。

难得的FFT20分钟1A。


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=200000;
const double pi=acos(-1.0);
typedef long long LL;

struct Complex
{
    double X,Y;
    Complex (double a=0.0,double b=0.0) : X(a),Y(b) {}
} ;

Complex operator+(Complex a,Complex b){return Complex(a.X+b.X,a.Y+b.Y);}
Complex operator-(Complex a,Complex b){return Complex(a.X-b.X,a.Y-b.Y);}
Complex operator*(Complex a,Complex b){return Complex(a.X*b.X-a.Y*b.Y,a.X*b.Y+a.Y*b.X);}

int Rev[maxn];
Complex A[maxn];
Complex B[maxn];

bool fx[maxn];
bool fy[maxn];
int numx,numy;

int px[maxn];
int py[maxn];
int t,n,r,c;
LL ans;
int N,Lg;

void DFT(Complex *a,double f)
{
    for (int i=0; i<N; i++)
        if (i<Rev[i]) swap(a[i],a[ Rev[i] ]);
    for (int len=2; len<=N; len<<=1)
    {
        int mid=len>>1;
        double ang=2.0*pi/((double)len);
        Complex e( cos(ang) , f*sin(ang) );
        for (Complex *p=a; p!=a+N; p+=len)
        {
            Complex wn(1.0,0.0);
            for (int i=0; i<mid; i++)
            {
                Complex temp=wn*p[mid+i];
                p[mid+i]=p[i]-temp;
                p[i]=p[i]+temp;
                wn=wn*e;
            }
        }
    }
}

void FFT()
{
    for (int i=0; i<N; i++)
    {
        Rev[i]=0;
        for (int j=0; j<Lg; j++)
            if ( i&(1<<j) ) Rev[i]|=( 1<<(Lg-j-1) );
    }
    DFT(A,1.0);
    DFT(B,1.0);
    for (int i=0; i<N; i++) A[i]=A[i]*B[i];
    DFT(A,-1.0);
    for (int i=0; i<N; i++) A[i].X/=((double)N);
}

int main()
{
    freopen("12633.in","r",stdin);
    freopen("12633.out","w",stdout);

    scanf("%d",&t);
    for (int g=1; g<=t; g++)
    {
        scanf("%d%d%d",&r,&c,&n);
        for (int i=1; i<=r; i++) fx[i]=false;
        for (int i=1; i<=c; i++) fy[i]=false;

        for (int i=1; i<=n; i++)
        {
            scanf("%d%d",&px[i],&py[i]);
            fx[ px[i] ]=true;
            fy[ py[i] ]=true;
        }

        numx=numy=0;
        for (int i=1; i<=r; i++) if (fx[i]) numx++;
        for (int i=1; i<=c; i++) if (fy[i]) numy++;
        ans=0;
        ans-=( (long long)numx*(long long)c+(long long)numy*(long long)r );
        ans+=( (long long)r*(long long)c+(long long)numx*(long long)numy );

        for (int i=0; i<r; i++)
            if (fx[i+1]) A[i]=Complex(0.0,0.0);
            else A[i]=Complex(1.0,0.0);
        for (int i=0; i<c; i++)
            if (fy[i+1]) B[i]=Complex(0.0,0.0);
            else B[i]=Complex(1.0,0.0);
        for (int i=0; i<c-i-1; i++) swap(B[i],B[c-i-1]);

        N=1,Lg=0;
        while (N<r+c) N<<=1,Lg++;
        for (int i=r; i<N; i++) A[i]=Complex(0.0,0.0);
        for (int i=c; i<N; i++) B[i]=Complex(0.0,0.0);

        FFT();

        for (int i=1; i<=n; i++)
        {
            int x=px[i]-1,y=py[i]-1;
            y=c-y-1;
            ans-=( (int)floor(A[x+y].X+0.5) );
            A[x+y].X=0.0;
        }
        printf("Case %d: %lld\n",g,ans);
    }

    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值