成都day1t3

3 、 数数 ( counting .pas /cpp )
【问题描述】
企鹅豆豆有个 N 行 M 列的矩阵,也就是有(N+1)×(M+1)个格点。
现在想知道格点可以组成多少个正方形?
但是企鹅豆豆不小心吃掉了 K 个格点。那么现在又有多少个正方形?
【输入格式】
第一行包含三个整数 N,M,K,代表棋盘的行数、列数和不能选取的顶点个数。 保证 K
≤(N+1)×(M+1)。
接下来 K 行,每行包含两个正整数 X,Y,代表第 X 行第 Y 列的格点被吃掉了。保证 0
≤X≤N,0≤Y≤M,且不会出现重复的格点。约定每行的格点从上到下依次用整数 0 到 N 编
号,每列的格点依次用 0 到 M 编号。
【输出格式】
输出一个正整数,代表正方形个数对 100000007 取模之后的数值。
【输入样例1】
2 2 4
1 0
1 2
0 1
2 1
【输出样例1】

1

【输入样例2】
7 10 5
2 3
1 5
6 2
3 5
2 6
【输出样例2】

429

【输入样例3】
2 2 4
0 0
2 2
0 2
2 0
【输出样例3】

1

【样例说明】

可以有斜着的正方形


分析:

注意模数是1e8+7!不是1e9+7!

这是一道简单容斥计数

答案=

整个网格内的正方形数

-恰好有1个点是不合法点的正方形数

-恰好有2个点是不合法点的正方形数

-恰好有3个点是不合法点的正方形数

-恰好有4个点是不合法点的正方形数

 

对于整个网格内的正方形,有斜着的和正着的,我们以正着的正方形来计数,

每个正方形的权值为其边长(也就是它里面可以有这么多个斜着的正方形),这个枚举边长统计个数计算即可。复杂度O(N)

 

对于恰好有2个3个4个点是不合法点的正方形个数可以通过枚举对角线或者临边O(K^2)统计。

 

对于恰好有1个的,我们可先统计至少有1个的,然后在扣除有2个3个4个的。

统计一个的方法可以先枚举每一个不合法点,然后统计以他为一角且在矩形底边,左侧空间有l,右侧空间有r,高度有h的可行方案数。

 

最后复杂度O(N + K^2)。

下面给出标程:

#include<bits/stdc++.h>
#define mod 100000007
#define maxn 2010
#define gets(x,y) (1LL*((x)+(y))*((y)-(x)+1)>>1)
using namespace std;
int n,m,cnt,ans,t1,t2,t3,t4,a[maxn],b[maxn];
struct node{
int tot,fst[2010527],px[maxn],py[maxn],nxt[maxn];
void insert(int x,int y){
int z=(x*97+y)%2010527;
px[++tot]=x;py[tot]=y;
nxt[tot]=fst[z];fst[z]=tot;
}
int find(int x,int y){
int z=(x*97+y)%2010527;
for(int p=fst[z];p;p=nxt[p])
if(px[p]==x && py[p]==y)return 1;
return 0;
}
}myhash;
bool inmp(int x,int y){return x>=0&&x<=m&&y>=0&&y<=n;}
void calc(int x,int y,int z){
if(!x||!y||z<2)return;
z=min(z,x+y);
x=min(x,z-1);
y=min(y,z-1);
t1=(t1+1LL*(z-y)*y)%mod;
t1=(t1+gets(z-x,y-1))%mod;
}
void update(int u1,int v1,int u2,int v2){
if(inmp(u1,v1)&&inmp(u2,v2)){
int tmp=myhash.find(u1,v1)+myhash.find(u2,v2);
t2++;t3+=tmp;
if(tmp>1)t4++;
}
}
void solve(int x1,int y1,int x2,int y2){
int dx=x2-x1,dy=y2-y1;
update(x1+dy,y1-dx,x2+dy,y2-dx);
update(x1-dy,y1+dx,x2-dy,y2+dx);
if(abs(dx+dy)&1)return;
    dy=(dx+dy)>>1;dx-=dy;
    update(x1+dx,y1+dy,x2-dx,y2-dy);
}
int main(){
freopen("counting.in","r",stdin);
freopen("counting.out","w",stdout);
scanf("%d%d%d",&m,&n,&cnt);
for(int i=1;i<=cnt;i++){
scanf("%d%d",&a[i],&b[i]);
myhash.insert(a[i],b[i]);
}
for(int i=1;i<=m && i<=n;i++)
ans=(ans+1LL*i*(m-i+1)%mod*(n-i+1))%mod;
for(int i=1;i<=cnt;i++){
calc(a[i],m-a[i],b[i]);
calc(a[i],m-a[i],n-b[i]);

calc(b[i],n-b[i],a[i]);
calc(b[i],n-b[i],m-a[i]);
t1=(t1+min(a[i],b[i])+min(a[i],n-b[i])+min(m-a[i],b[i])+min(m-a[i],n-b[i]))%mod;
for(int j=1;j<i;j++)
solve(a[i],b[i],a[j],b[j]);
}
printf("%d",(ans-t1+t2-t3/3+t4/6+mod)%mod);
return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值