题目描述
上帝说,不要圆,要方,于是便有了这道题。
由于我们应该方,而且最好能够尽量方,所以上帝派我们来找正方形上帝把我们派到了一个有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
*/