bzoj 4558: [JLoi2016]方 数学&计数

本文介绍了一种利用容斥原理解决包含多个坏点的正方形计数问题的方法。通过枚举不同情况下的正方形框架及其包含的坏点数量,实现了有效计算。文章提供了详细的AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       首先通过容斥转化为求:所有正方形-至少有1个坏点的正方形+至少有2个的-至少有3个的+有4个的。

       所有正方形:显然一个正方形不管是斜的还是正的,它所占的网格中的空间一定是一个正的正方形,不妨称为该正方形的框架。于是我们可以枚举这个正方形的框架的边长,然后枚举偏离多少格即可。

       至少有1个坏点:我们枚举坏点,然后同样枚举包含这个坏点的正方形的框架,那么这个坏点在框架上的位置,共两个大类(在边上不在角上和在角上),八个小类(四条边四个角)分别统计一下即可。

       至少有2个坏点:枚举2个坏点,此时正方形就是固定的共三种形态,算出三种形态的四个角统计即可。可以顺便把含3,4个坏点的一起统计。

       最后注意一个正方形可能被多次统计。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define ll long long
#define mod 100000007
#define gets(x,y) ((ll)((x)+(y))*((y)-(x)+1)>>1)
#define N 2005
using namespace std;

int m,n,cnt,ans,t1,t2,t3,t4,a[N],b[N];
struct hsh_node{
	int tot,fst[2010527],px[N],py[N],nxt[N];
	void ins(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,p;
		for (p=fst[z]; p; p=nxt[p])
			if (px[p]==x && py[p]==y) return 1;
		return 0;
	}
}hsh;
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+(ll)(z-y)*y)%mod;
	t1=(t1+gets(z-x,y-1))%mod;
}
void updt(int u1,int v1,int u2,int v2){
	if (inmp(u1,v1) && inmp(u2,v2)){
		int tmp=hsh.find(u1,v1)+hsh.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;
	updt(x1+dy,y1-dx,x2+dy,y2-dx);
	updt(x1-dy,y1+dx,x2-dy,y2+dx);
	if (abs(dx+dy)&1) return;
	dy=(dx+dy)>>1; dx-=dy;
	updt(x1+dx,y1+dy,x2-dx,y2-dy);
}
int main(){
	scanf("%d%d%d",&m,&n,&cnt); int i,j;
	for (i=1; i<=cnt; i++){
		scanf("%d%d",&a[i],&b[i]);
		hsh.ins(a[i],b[i]);
	}
	for (i=1; i<=m && i<=n; i++)
		ans=(ans+(ll)i*(m-i+1)%mod*(n-i+1))%mod;
	for (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 (j=1; j<i; j++) solve(a[i],b[i],a[j],b[j]);
	}
	printf("%d\n",(ans-t1+t2-t3/3+t4/6+mod)%mod);
	return 0;
}


by lych

2016.5.23

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值