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

211 篇文章 0 订阅
78 篇文章 0 订阅

题目描述

传送门

题解

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

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

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
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 x<a.x||(x==a.x&&y<a.y);
    }
}p[N];
int 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].x<x) l=mid+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].x<x) l=mid+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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值