Bzoj4558 [JLoi2016]方
2017-06-01 21:17
369 查看
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 41 0
1 2
0 1
2 1
Sample Output
1HINT
Source
数学问题 容斥
不考虑坏点限制,枚举正方形的长度即可算出所有正方形的数量。
ans=总数-至少有一个坏点的正方形+至少有两个坏点的正方形-至少有三个坏点的正方形+至少有四个坏点的正方形
枚举两个坏点作为正方形顶点,可以算出另外两个顶点的坐标,用hash可以判断两顶点是不是坏点,从而累计后三部分答案。
至少有一个坏点的正方形怎么求?
假设这个坏点在一个正方形的边上,枚举正方形的长度,综合考虑四个方向的长度限制,可以计算出方案数。
前半个小时看错题,以为求内部没有坏点的正方形方案数,一脸懵X
推错一次式子,又删了代码从头开始写……
各种心累。
/*by SilverN*/ #include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> #define LL long long using namespace std; const int mod=1e8+7; const int mxn=10010; 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*10+ch-'0';ch=getchar();} return x*f; } const int hsmod=100007; struct hs_eg{LL z;int nxt;}e[mxn<<1]; int hd[hsmod+2],Hct=0; void hs_insert(LL x){ int u=x%hsmod; for(int i=hd[u];i;i=e[i].nxt){if(e[i].z==x)return;} e[++Hct].z=x;e[Hct].nxt=hd[u];hd[u]=Hct;return; return; } int hs_find(LL x){ int u=x%hsmod; for(int i=hd[u];i;i=e[i].nxt)if(e[i].z==x)return 1; return 0; } // int n,m,K; int x[mxn],y[mxn]; LL ans; int Calc(int lenU,int lenD,int lenR){ LL res=-min(lenU,lenR); res=res+min(lenR,lenU+lenD); int r1=lenU,r2=lenD,r3=lenR; if(r1>r2)swap(r1,r2); if(r3<=r1)return ((LL)res+r3*(LL)(r3+1)/2)%mod; res=((LL)res+r1*(LL)(r1+1)/2)%mod; if(r3<=r2)return ((LL)res+r1*(LL)(r3-r1)%mod)%mod; res=((LL)res+r1*(LL)(r2-r1)%mod)%mod; if(r3<=r1+r2) return (res+(LL)(r3-r2)*r1-(LL)(r3-r2)*(r3-r2+1)/2)%mod; res=(res+(LL)r1*(r1-1)/2)%mod; return res; } // int res2=0,res3=0,res4=0; inline bool check(int x,int y){ return (x>=0 && x<=n && y>=0 && y<=m); } void UPD(int x1,int y1,int x2,int y2){ if(check(x1,y1) && check(x2,y2)){ bool flag1=0,flag2=0; res2++; if(hs_find((LL)x1*(m+1)+y1))flag1=1;//,printf("(%d %d %lld)\n",x1,y1,(LL)x1*m+y1); if(hs_find((LL)x2*(m+1)+y2))flag2=1;//,printf("(%d %d %lld)\n",x2,y2,(LL)x2*m+y2); if(flag1)res3++;if(flag2)res3++; if(flag1 & flag2)res4++; } return; } int main(){ freopen("in.txt","r",stdin); int i,j; n=read();m=read();K=read(); for(i=1;i<=K;i++){ x[i]=read();y[i]=read(); hs_insert((LL)x[i]*(m+1)+y[i]); } int ed=min(n,m); for(i=1;i<=ed;i++)ans=((LL)ans+i*(LL)(n-i+1)%mod*(m-i+1))%mod;//total // LL res=0; //Calc1 for(i=1;i<=K;i++){ res=0; res=res+Calc(x[i],n-x[i],y[i]); res=(res+Calc(n-x[i],x[i],m-y[i]))%mod; res=(res+Calc(y[i],m-y[i],n-x[i]))%mod; res=(res+Calc(m-y[i],y[i],x[i]))%mod; ans=(ans-res+mod)%mod; } //Calc2 3 4 for(i=1;i<K;i++){ for(j=i+1;j<=K;j++){ int tmpx=x[j]-x[i]; int tmpy=y[j]-y[i]; UPD(x[i]+tmpy,y[i]-tmpx,x[j]+tmpy,y[j]-tmpx); UPD(x[i]-tmpy,y[i]+tmpx,x[j]-tmpy,y[j]+tmpx); if(abs(tmpx+tmpy)&1)continue; tmpy=(tmpx+tmpy)>>1; tmpx-=tmpy; UPD(x[i]+tmpx,y[i]+tmpy,x[j]-tmpx,y[j]-tmpy); } }// // printf("ans:%lld\n",ans); // printf("%d %d %d\n",res2,res3/3,res4/6); ans=(((LL)ans+res2-res3/3+res4/6)%mod+mod)%mod; printf("%lld\n",ans); return 0; }
相关文章推荐
- BZOJ 4558 [JLoi2016] 方
- [bzoj4558][JLoi2016]方【容斥原理】【计数】
- bzoj4558[JLoi2016]方 容斥+count
- 【BZOJ 4558】 4558: [JLoi2016]方 (计数、容斥原理)
- BZOJ4558 [JLoi2016]方
- bzoj 4558: [JLoi2016]方 数学&计数
- BZOJ4558: [JLoi2016]方
- 【JLOI2016】bzoj4558 方
- bzoj千题计划281:bzoj4558: [JLoi2016]方
- [BZOJ4558] [JLoi2016]方
- bzoj4558 [JLoi2016]方
- BZOJ 4558|JLOI 2016|SHOI 2016|方|容斥原理
- [BZOJ4558][JLoi2016]方(数学相关+容斥原理)
- 【BZOJ】4558: [JLoi2016]方
- BZOJ4557 [JLoi2016]侦察守卫 【树形dp】
- bzoj 4557 [JLoi2016]侦察守卫
- [BZOJ4561][JLoi2016]圆的异或并(扫描线+splay)
- 【BZOJ4557】[JLoi2016]侦察守卫 树形DP
- 【BZOJ 4561】【JLOI 2016】圆的异或并
- bzoj 4559: [JLoi2016]成绩比较 dp+拉格朗日插值