Bzoj4558 [JLoi2016]方
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 382 Solved: 173
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
1 0
1 2
0 1
2 1
Sample Output
1
HINT
Source
数学问题 容斥
不考虑坏点限制,枚举正方形的长度即可算出所有正方形的数量。
ans=总数-至少有一个坏点的正方形+至少有两个坏点的正方形-至少有三个坏点的正方形+至少有四个坏点的正方形
枚举两个坏点作为正方形顶点,可以算出另外两个顶点的坐标,用hash可以判断两顶点是不是坏点,从而累计后三部分答案。
至少有一个坏点的正方形怎么求?
假设这个坏点在一个正方形的边上,枚举正方形的长度,综合考虑四个方向的长度限制,可以计算出方案数。
前半个小时看错题,以为求内部没有坏点的正方形方案数,一脸懵X
推错一次式子,又删了代码从头开始写……
各种心累。
1 /*by SilverN*/ 2 #include<iostream> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cmath> 6 #include<cstring> 7 #define LL long long 8 using namespace std; 9 const int mod=1e8+7; 10 const int mxn=10010; 11 int read(){ 12 int x=0,f=1;char ch=getchar(); 13 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 14 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 15 return x*f; 16 } 17 const int hsmod=100007; 18 struct hs_eg{LL z;int nxt;}e[mxn<<1]; 19 int hd[hsmod+2],Hct=0; 20 void hs_insert(LL x){ 21 int u=x%hsmod; 22 for(int i=hd[u];i;i=e[i].nxt){if(e[i].z==x)return;} 23 e[++Hct].z=x;e[Hct].nxt=hd[u];hd[u]=Hct;return; 24 return; 25 } 26 int hs_find(LL x){ 27 int u=x%hsmod; 28 for(int i=hd[u];i;i=e[i].nxt)if(e[i].z==x)return 1; 29 return 0; 30 } 31 // 32 int n,m,K; 33 int x[mxn],y[mxn]; 34 LL ans; 35 int Calc(int lenU,int lenD,int lenR){ 36 LL res=-min(lenU,lenR); 37 res=res+min(lenR,lenU+lenD); 38 int r1=lenU,r2=lenD,r3=lenR; 39 if(r1>r2)swap(r1,r2); 40 if(r3<=r1)return ((LL)res+r3*(LL)(r3+1)/2)%mod; 41 res=((LL)res+r1*(LL)(r1+1)/2)%mod; 42 if(r3<=r2)return ((LL)res+r1*(LL)(r3-r1)%mod)%mod; 43 res=((LL)res+r1*(LL)(r2-r1)%mod)%mod; 44 if(r3<=r1+r2) return (res+(LL)(r3-r2)*r1-(LL)(r3-r2)*(r3-r2+1)/2)%mod; 45 res=(res+(LL)r1*(r1-1)/2)%mod; 46 return res; 47 } 48 // 49 int res2=0,res3=0,res4=0; 50 inline bool check(int x,int y){ 51 return (x>=0 && x<=n && y>=0 && y<=m); 52 } 53 void UPD(int x1,int y1,int x2,int y2){ 54 if(check(x1,y1) && check(x2,y2)){ 55 bool flag1=0,flag2=0; 56 res2++; 57 if(hs_find((LL)x1*(m+1)+y1))flag1=1;//,printf("(%d %d %lld)\n",x1,y1,(LL)x1*m+y1); 58 if(hs_find((LL)x2*(m+1)+y2))flag2=1;//,printf("(%d %d %lld)\n",x2,y2,(LL)x2*m+y2); 59 if(flag1)res3++;if(flag2)res3++; 60 if(flag1 & flag2)res4++; 61 } 62 return; 63 } 64 int main(){ 65 freopen("in.txt","r",stdin); 66 int i,j; 67 n=read();m=read();K=read(); 68 for(i=1;i<=K;i++){ 69 x[i]=read();y[i]=read(); 70 hs_insert((LL)x[i]*(m+1)+y[i]); 71 } 72 int ed=min(n,m); 73 for(i=1;i<=ed;i++)ans=((LL)ans+i*(LL)(n-i+1)%mod*(m-i+1))%mod;//total 74 // 75 LL res=0; 76 //Calc1 77 for(i=1;i<=K;i++){ 78 res=0; 79 res=res+Calc(x[i],n-x[i],y[i]); 80 res=(res+Calc(n-x[i],x[i],m-y[i]))%mod; 81 res=(res+Calc(y[i],m-y[i],n-x[i]))%mod; 82 res=(res+Calc(m-y[i],y[i],x[i]))%mod; 83 ans=(ans-res+mod)%mod; 84 } 85 //Calc2 3 4 86 for(i=1;i<K;i++){ 87 for(j=i+1;j<=K;j++){ 88 int tmpx=x[j]-x[i]; 89 int tmpy=y[j]-y[i]; 90 UPD(x[i]+tmpy,y[i]-tmpx,x[j]+tmpy,y[j]-tmpx); 91 UPD(x[i]-tmpy,y[i]+tmpx,x[j]-tmpy,y[j]+tmpx); 92 if(abs(tmpx+tmpy)&1)continue; 93 tmpy=(tmpx+tmpy)>>1; 94 tmpx-=tmpy; 95 UPD(x[i]+tmpx,y[i]+tmpy,x[j]-tmpx,y[j]-tmpy); 96 } 97 }// 98 // printf("ans:%lld\n",ans); 99 // printf("%d %d %d\n",res2,res3/3,res4/6); 100 ans=(((LL)ans+res2-res3/3+res4/6)%mod+mod)%mod; 101 printf("%lld\n",ans); 102 return 0; 103 }
本文为博主原创文章,转载请注明出处。