洛谷 P3271 [JLOI2016]方

题目描述

上帝说,不要圆,要方,于是便有了这道题。

由于我们应该方,而且最好能够尽量方,所以上帝派我们来找正方形上帝把我们派到了一个有N行M列的方格图上,图上一共有(N+1)*(M+1)个格点,我们需要做的就是找出这些格点形成了多少个正方形(换句话说,正方形的四个顶点都是格点)。

但是这个问题对于我们来说太难了,因为点数太多了,所以上帝删掉了这(N+1)*(M+1)中的K个点。既然点变少了,问题也就变简单了,那么这个时候这些格点组成了多少个正方形呢?
输入输出格式
输入格式:

第一行三个整数 N, M, K, 代表棋盘的行数、 列数和不能选取的顶点个数。 保证 N, M >= 1, K <=(N + 1) ×(M + 1)。

约定每行的格点从上到下依次用整数 0 到 N 编号,每列的格点依次用 0到 M 编号。

接下来 K 行,每行两个整数 x,y 代表第 x 行第 y 列的格点被删掉了。

保证 0 <=x <=N<=10^6, 0 <=y<=M<=10^6,K<=2*1000且不会出现重复的格点。

输出格式:

仅一行一个正整数, 代表正方形个数对 100000007( 10^8 + 7) 取模之后的值

输入输出样例
输入样例#1:

2 2 4
1 0
1 2
0 1
2 1

输出样例#1:

1


【分析】
真的好题…
经过wxl与lyx与yny(提供不冲突hash的办法)与czy的共同努力,一道神题终于被肝出来了…
题解现在懒得写啦…快省选了…抓紧时间做题(日后会更题解的)

主要思路=容斥原理+hash


【代码】

//洛谷 P3271 [JLOI2016]方
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(register int i=j;i<=k;++i)
using namespace std;
const int mod=1e8+7,M=1e6+4;
ll ans,thr,tmp,four;
int n,m,p,tot;
int head[1000005];
struct hash {int next;ll val;} f[1000005];
struct node {int x,y;} a[20005];
inline ll Q(int i,int j)
{
    return (ll)i*(m+1)+(ll)j;
}
inline ll P(int i,int j)
{
    return ((ll)i*(m+1)+(ll)j)%M;
}
inline void add(int u,ll val)
{
    f[++tot].next=head[u],f[tot].val=val,head[u]=tot;
}
inline void yny(int len,int mid,int up,int rlt)   //up卡位,rlt变身 
{
    int i,j;
    if(up<=mid)
    {
        if(up<=rlt) ans=(ans-(ll)(1+up)*up/2)%mod; 
        else ans=(ans-(ll)(1+rlt)*rlt/2)%mod,ans=(ans-(ll)(up-rlt)*rlt)%mod;
    }
    else
    {
        int down=len-rlt+1,czy=len-up+1;
        if(up<down) ans=(ans-(ll)(1+rlt)*rlt/2)%mod,ans=(ans-(ll)(up-rlt)*rlt)%mod;
        else ans=(ans-(ll)(down-rlt+1)*rlt)%mod,ans=(ans-(ll)rlt*(rlt-1)/2)%mod,ans=(ans-(ll)(czy+rlt-1)*(rlt-czy)/2)%mod;
    }
}
inline bool lyx(ll u,ll v)
{
    for(int i=head[u];i;i=f[i].next)
      if(f[i].val==v) return 1;
    return 0;
}
int main()
{
//  freopen("ac.in","r",stdin);
    int up,len,mid,rlt;
    scanf("%d%d%d",&n,&m,&p);
    fo(i,1,min(n,m))
      ans=(ans+(ll)i*(ll)(n-i+1)*(ll)(m-i+1))%mod;

//    printf("all=%lld\n",ans);
    fo(i,1,p)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        add(P(a[i].x,a[i].y),Q(a[i].x,a[i].y));
    }
    fo(k,1,p)   //一个坏点 
    {
        up=min(n-1,a[k].y-1),len=n-1,mid=n>>1,rlt=min(a[k].x,n-a[k].x);   //1.卡右边(不含端点) 总共有up种正方形
        if(up>0) yny(len,mid,up,rlt);
        up=min(n-1,m-a[k].y-1);
        if(up>0) yny(len,mid,up,rlt);
        up=min(m-1,a[k].x-1),len=m-1,mid=m>>1,rlt=min(a[k].y,m-a[k].y);   //下边界 
        if(up>0) yny(len,mid,up,rlt);
        up=min(m-1,n-a[k].x-1);
        if(up>0) yny(len,mid,up,rlt);
        //顶点四种情况 
        ans=(ans-min(a[k].x,a[k].y))%mod;
        ans=(ans-min(n-a[k].x,a[k].y))%mod;
        ans=(ans-min(a[k].x,m-a[k].y))%mod;
        ans=(ans-min(n-a[k].x,m-a[k].y))%mod;
    }
    fo(i,1,p)    //两个坏点 
      fo(j,i+1,p)
      {
          int x1=a[i].x,y1=a[i].y,x2=a[j].x,y2=a[j].y;
          int x3=x1-(y2-y1),y3=y1+(x2-x1);
          int x4=x2-(y2-y1),y4=y2+(x2-x1);
          if(x3>=0 && x3<=n && y3>=0 && y3<=m && x4>=0 && x4<=n && y4>=0 && y4<=m)
          {
              ans++;
              if(lyx(P(x3,y3),Q(x3,y3))) thr++;
              if(lyx(P(x4,y4),Q(x4,y4))) thr++;
              if(lyx(P(x3,y3),Q(x3,y3)) && lyx(P(x4,y4),Q(x4,y4))) four++;
          } 
          x3=x1+(y2-y1),y3=y1-(x2-x1);
          x4=x2+(y2-y1),y4=y2-(x2-x1);
          if(x3>=0 && x3<=n && y3>=0 && y3<=m && x4>=0 && x4<=n && y4>=0 && y4<=m)
          {
              ans++;
              if(lyx(P(x3,y3),Q(x3,y3))) thr++;
              if(lyx(P(x4,y4),Q(x4,y4))) thr++;
              if(lyx(P(x3,y3),Q(x3,y3)) && lyx(P(x4,y4),Q(x4,y4))) four++;
          }
          int y=y2-y1,x=x2-x1;
          if((y-x)&1) continue;
          x3=x1-(y-x)/2,y3=y2-(y-x)/2,x4=x2+(y-x)/2,y4=y1+(y-x)/2;
          if(x3>=0 && x3<=n && y3>=0 && y3<=m && x4>=0 && x4<=n && y4>=0 && y4<=m)
          {
              ans++;
              if(lyx(P(x3,y3),Q(x3,y3))) thr++;
              if(lyx(P(x4,y4),Q(x4,y4))) thr++;
              if(lyx(P(x3,y3),Q(x3,y3)) && lyx(P(x4,y4),Q(x4,y4))) four++;
          }
      }
    ans=(ans-thr/3+four/6)%mod;

//    printf("sec=%d\n",tmp);
//    printf("thr=%lld\n",thr/3);
//    printf("four=%lld\n",four/6);

    printf("%lld\n",(ans+mod)%mod);
    return 0;
}
/*
5 2 8
1 2
0 0
2 1
3 0
5 0
4 1
3 2
1 0
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值