UVA12633(用于计数的FFT)

V-judge题面

题意就是有一个R*C的矩阵,上面有一些点。每个点能控制所在行,所在列和所在主对角线(从左上到右下的一条对角线),问整个矩阵有多少个点没有被控制。

如果没有主对角线,则ANS=没被控制的行数*没被控制的列数。

根据主对角线的性质,一条主对角线上的点的横纵坐标的差为定值,设为k,那么就可以用k代表一条主对角线。

我们可以枚举每条被控制的主对角线k,要知道的便是k上有多少点没有被控制,设为f[k]。
再构造两个函数,a[i]为第i行是否被控制,若被控制则为0,否则为1。b[i]为第i列是否被控制,若被控制则为0,否则为1。就有

f[k]=i=max(0,k)Ra[i]b[ik]

就是一个卷积了,上FFT就可以了。
学完牛顿迭代法,伯努利数…这些幂级数的东西,就可以摆脱FFT了去学搜索了。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=200200;
const LL p=1004535809,g=3;

int n,rev[N];

LL cheng(LL a,LL b)
{
    LL res=1ll;
    for(;b;b>>=1,a=a*a%p)
    if(b&1)
    res=res*a%p;
    return res;
}

void init(int lim)
{
    n=1;
    int k=-1;
    while(n<=lim)
    n<<=1,k++;
    for(int i=0;i<n;i++)
    rev[i]=(rev[i>>1]>>1) | ((i&1)<<k);
}

void ntt(LL *a,int ops)
{
    for(int i=0;i<n;i++)
    if(i<rev[i])
    swap(a[i],a[rev[i]]);
    for(int l=2;l<=n;l<<=1)
    {
        int m=l>>1;
        LL wn;
        if(ops)
        wn=cheng(g,(p-1)/l);
        else
        wn=cheng(g,p-1-(p-1)/l);
        for(int i=0;i<n;i+=l)
        {
            LL w=1ll;
            for(int k=0;k<m;k++)
            {
                LL t=a[i+k+m]*w%p;
                a[i+k+m]=(a[i+k]-t+p)%p;
                a[i+k]=(a[i+k]+t)%p;
                w=w*wn%p;
            }
        }
    }
    if(!ops)
    {
        LL Inv=cheng(n,p-2);
        for(int i=0;i<n;i++)
        a[i]=a[i]*Inv%p;
    }
}

LL ans;
int T,mx,R,C,nn;
LL aa[N],bb[N],cc[N];
int hang,lie;
bool dj[N];

int main()
{
    cin>>T;
    for(int ii=1;ii<=T;ii++)
    {

    cin>>R>>C>>nn;
    hang=R;
    lie=C;
    mx=max(R,C);    
    R--;
    C--;

    init(R+C+5);
    for(int i=0;i<n;i++)
    aa[i]=bb[i]=cc[i]=0;
    mmst(dj,0);

    for(int i=0;i<=R;i++)
    aa[i]=1;
    for(int i=0;i<=C;i++)
    bb[i]=1;

    for(int i=1;i<=nn;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x--;
        y--;
        dj[x-y+mx]=1;

        if(aa[x])
        hang--;
        if(bb[y])
        lie--;

        aa[x]=0;
        bb[y]=0;
    }

    for(int i=0;i<=C;i++)
    cc[i]=bb[C-i];  

    ntt(aa,1);
    ntt(cc,1);
    for(int i=0;i<n;i++)
    aa[i]=aa[i]*cc[i]%p;
    ntt(aa,0);

    ans=(LL)hang*lie;

    for(int i=0;i<n;i++)
    if(dj[i-C+mx])
    ans-=aa[i];

    cout<<"Case "<<ii<<": "<<ans<<endl;
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值