[BZOJ4558][JLoi2016]方(数学相关+容斥原理)

题目描述

传送门

题解

这题有毒啊…
首先容斥一下
答案=整个网格内的正方形数-至少有1个点是不合法点的正方形数+恰好有2个点是不合法点的正方形数*2+恰好有3个点是不合法点的正方形数-恰好有4个点是不合法点的正方形数

整个网格内的正方形数看似不好算,因为有正着斜着的正方形,但是可以发现每一个正方形都是由其外接正方形决定的,也就是可以枚举外接正方形的边长,一个边长为a的外接正方形可以确定出来a个正方形
恰好有2个、3个、4个点是不合法点的正方形数可以通过枚举两个不合法点,然后将这两个点当做某一条边的端点、对角线的端点确定出来3个正方形,然后计算就行了。得知了两个点的坐标是可以通过横纵坐标的增减量计算出来剩下两个点的左坐标。
关键是至少有一个点是不合法点的正方形数不大好算。首先可以枚举一个不合法点,但是一个顶点是这个点 的正方形数怎么算呢?
经过n次失败的尝试之后终于看了题解,贴上一个感觉还挺好的讲解:
http://blog.csdn.net/huanghongxun/article/details/51267460

代码

#include
#include
#include
#include
#include
using namespace std;
#define Mod 100000007
#define LL long long
#define N 2005
#define gets(x,y) ((LL)((x)+(y))*((y)-(x)+1)>>1)

struct data
{
    int x,y;
    bool operator < (const data &a) const
    {
        return xint n,m,k,tot[5];
LL ans;

LL calc(int l,int r,int h)
{
    LL ans=0;
    int z=min(l+r,h);
    if (!z) return 0;
    ans=(LL)z*(z+3)/2;
    if (z>l) ans-=(LL)(z-l)*(z-l+1)/2;
    if (z>r) ans-=(LL)(z-r)*(z-r+1)/2;
    return ans%Mod;
}
LL calc_one()
{
    LL ans=0;
    for (int i=1;i<=k;++i)
    {
        int x=p[i].x,y=p[i].y;
        ans+=calc(x-1,n-x,y-1);
        ans+=calc(x-1,n-x,m-y);  
        ans+=calc(y-1,m-y,x-1);
        ans+=calc(y-1,m-y,n-x);  
        ans=(ans-min(x-1,y-1)-min(x-1,m-y)-min(n-x,y-1)-min(n-x,m-y))%Mod;
    }
    return ans;
}
int findl(int x)
{
    int l=1,r=k,mid,ans=-1;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (p[mid].x==x) ans=mid,r=mid-1;
        else if (p[mid].xmid+1;
        else r=mid-1;
    }
    return ans;
}
int findr(int x)
{
    int l=1,r=k,mid,ans=-1;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (p[mid].x==x) ans=mid,l=mid+1;
        else if (p[mid].xmid+1;
        else r=mid-1;
    }
    return ans;
}
int find(int l,int r,int x)
{
    int mid,ans=0;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (p[mid].y==x) return mid;
        else if (p[mid].y>x) r=mid-1;
        else l=mid+1;
    }
    return ans;
}
bool check(data now)
{
    int l=findl(now.x);
    int r=findr(now.x);
    if (l==-1||r==-1) return 0;
    if (find(l,r,now.y)) return 1;
    else return 0;
}
LL calc_ttf()
{
    for (int i=1;i<=k;++i)
        for (int j=i+1;j<=k;++j)
        {
            int a=p[i].x,b=p[i].y,c=p[j].x,d=p[j].y,x,y,r,s,t,u,f,g;
            int cnt;
            data now;

            x=c-a,y=d-b; 
            r=a+y,s=b-x,t=c+y,u=d-x;
            if (r>0&&r<=n&&s>0&&s<=m&&t>0&&t<=n&&u>0&&u<=m)
            {
                cnt=2;
                now.x=r,now.y=s;
                if (check(now)) ++cnt;
                now.x=t,now.y=u;
                if (check(now)) ++cnt;
                ++tot[cnt];
                if (tot[cnt]==Mod) tot[cnt]=0;
            }

            x=c-a,y=d-b; 
            r=a-y,s=b+x,t=c-y,u=d+x;
            if (r>0&&r<=n&&s>0&&s<=m&&t>0&&t<=n&&u>0&&u<=m)
            {
                cnt=2;
                now.x=r,now.y=s;
                if (check(now)) ++cnt;
                now.x=t,now.y=u;
                if (check(now)) ++cnt;
                ++tot[cnt];
                if (tot[cnt]==Mod) tot[cnt]=0;
            }

            x=c-a;y=d-b;
            if ((y-x)%2!=0) continue;
            g=(y-x)/2;
            f=g-y;
            r=c+f,s=d-g,t=c+g,u=d+f;

            if (r>0&&r<=n&&s>0&&s<=m&&t>0&&t<=n&&u>0&&u<=m)
            {
                cnt=2;
                now.x=r,now.y=s;
                if (check(now)) ++cnt;
                now.x=t,now.y=u;
                if (check(now)) ++cnt;
                ++tot[cnt];
                if (tot[cnt]==Mod) tot[cnt]=0;
            }
        }
    tot[3]/=3,tot[4]/=6;
    return (tot[2]+tot[3]*2+tot[4]*3)%Mod;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);++n,++m;
    for (int i=1;i<=k;++i)
    {
        scanf("%d%d",&p[i].x,&p[i].y);
        ++p[i].x,++p[i].y;
    }
    sort(p+1,p+k+1);
    for (int i=2;i<=min(n,m);++i)
    {
        ans+=(LL)(n-i+1)*(m-i+1)%Mod*(i-1)%Mod;
        ans%=Mod;
    }
    ans-=calc_one();
    ans+=calc_ttf();
    ans=(ans%Mod+Mod)%Mod;
    printf("%lld\n",ans);
}

你可能感兴趣的:(题解,数学相关,省选,容斥原理)