您的位置:首页 > 编程语言 > C语言/C++

BZOJ 4558 [JLoi2016] 方

2017-06-02 20:16 483 查看

Description

上帝说,不要圆,要方,于是便有了这道题。由于我们应该方,而且最好能够尽量方,所以上帝派我们来找正方形
上帝把我们派到了一个有N行M列的方格图上,图上一共有(N+1)×(M+1)个格点,我们需要做的就是找出这些格点形
成了多少个正方形(换句话说,正方形的四个顶点都是格点)。但是这个问题对于我们来说太难了,因为点数太多
了,所以上帝删掉了这(N+1)×(M+1)中的K个点。既然点变少了,问题也就变简单了,那么这个时候这些格点组成
了多少个正方形呢?

Input

第一行三个整数 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且不
会出现重复的格点。

Output

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

Sample Input

2 2 4

1 0

1 2

0 1

2 1

Sample Output

1

HINT

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~

容斥原理~

答案可以看成是所有正方形数-至少有1个坏点+至少有2个坏点-至少有3个坏点+至少有4个坏点。

所有正方形数:斜的正方形可以补全为正的正方形,所以一个边长为n的正方形框架里面一共有n个正方形。

至少有1个坏点:分为坏点在正方形四角上和四边上(四边上即在斜的正方形的角上),分别计算。

剩下的情况:枚举两个坏点,就可以确定出三个正方形(注意两点是对角线时要判断是不是整点正方形),判断其余两个点是不是坏点,注意这里所有枚举都是[至少有]。

取模好奇怪啊……把(ans+(m-i+1)*(n-i+1)%modd*i%modd)%modd里面i后面的%modd去掉,就从WA到A了QAQ

最后注意x3/3,x4/6(重复枚举)。

(写代码的时候重复用了变量a,居然没有报错导致WA了好久,以后要注意!)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long
const ll modd=1e8+7;

int n,m,k,ni3,ni6;
ll ans,x1,x2,x3,x4;

struct node{
int x,y;
}a[2001];

map<node,bool> id;

bool operator < (node u,node v)
{
return u.x==v.x ? u.y<v.y:u.x<v.x;
}

int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}

bool kk(node u)
{
return u.x>=0 && u.x<=n && u.y>=0 && u.y<=m;
}

void add(node a,node b)
{
map<node,bool>::iterator a1,b1;
a1=id.find(a);b1=id.find(b);x2++;
if(a1!=id.end() || b1!=id.end()) x3++;
if(a1!=id.end() && b1!=id.end()) x4++;
}

void cal(int u,int v)
{
int x1=a[u].x,x2=a[v].x,y1=a[u].y,y2=a[v].y,k1,k2;
node a1,b1;
a1=(node){x1+y1-y2,y1+x2-x1};
b1=(node){x2+y1-y2,y2+x2-x1};
if(kk(a1) && kk(b1)) add(a1,b1);
a1=(node){2*x1-a1.x,2*y1-a1.y};
b1=(node){2*x2-b1.x,2*y2-b1.y};
if(kk(a1) && kk(b1)) add(a1,b1);
if((abs(x1-x2)+abs(y1-y2))&1) return;
k1=(x1-y1-x2+y2)/2;k2=(x1+y1-x2-y2)/2;
a1=(node){x1-k1,y1-k2};
b1=(node){x2+k1,y2+k2};
if(kk(a1) && kk(b1)) add(a1,b1);
}

ll num(int u,int v,int k)
{
ll z=min(u+v,k),now;
if(!z) return 0;
now=(z+3)*z/2;
if(z>u) now-=(z-u)*(z-u+1)/2;
if(z>v) now-=(z-v)*(z-v+1)/2;
return now;
}

ll cal0(node u)
{
int x=u.x,y=u.y,a=x,b=n-x,c=y,d=m-y;
return (num(a,b,c)+num(a,b,d)+num(c,d,a)+num(c,d,b)-min(a,c)-min(a,d)-min(b,c)-min(b,d)+modd)%modd;
}

int main()
{
n=read();m=read();k=read();
for(int i=min(n,m);i;i--) ans=(ans+(m-i+1)*(n-i+1)%modd*i)%modd;
for(int i=1;i<=k;i++)
{
a[i].x=read();a[i].y=read();id[a[i]]=1;
ans=(ans-cal0(a[i]))%modd;
}
for(int i=1;i<k;i++)
for(int j=i+1;j<=k;j++) cal(i,j);
ans=(ans+x2+x4/6-x3/3+modd)%modd;
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息