问题: 方

时间限制: 2 Sec  内存限制: 256 MB

题面


题目描述

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

输入格式

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

输出格式

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

样例输入

2 2 4

1 0

1 2

0 1

2 1

样例输出

1

题解


此题有毒!卡了我一整天。

从昨天下午一点多开始一直到今天的十一点,我才A掉了这到毒瘤题……

细节超多。

我们直接统计没有坏点的正方形个数显然不现实。暴力……没敢想。最起码$O(n^4)$的吧……

所以需要容斥原理。

我们处理出至少有零个坏点的正方形个数sum0,至少有一个坏点的正方形个数sum1,以此类推sum2、sum3和sum4的含义。

然后考虑怎么处理。

处理sum0:

我们通过观察得出这样的性质:

1.每一个斜正方形必定有唯一确定的包含它的最小的正正方形。

2.每一个边长为x的正正方形中包含着包括它自身共x个正方形。

于是我们推出式子:

$\sum\limits_{i=1}^{min(n,m)}{i*(n-i+1)*(m-i+1)}$

(i枚举边长,$(n-i+1)和(m-i+1)$的意义为平移后的个数。详情参见本人题解:数三角形)

然后就可以在O(n)时间内求出sum0。

处理sum1:

这道题最精髓(毒瘤)的部分。(查了若干份题解才搞明白还有一大堆结论没证……)

依然通过观察和理性思考,我们发现这样一个性质:

如果一个坏点在某一个正正方形的边上或者端点上,

那么它唯一对应这个正正方形中存在的某一个顶点均在正正方形边上的正方形(斜正方形或正正方形本身)

于是我们可以只去考虑该坏点位于正正方形的边上或者点上的情况。

问题转化为:过该坏点的正正方形有多少个。

枚举每一个坏点,我们考虑分情况讨论:该坏点在某个正方形的四个点上或四个边上。

四个顶点好求。由于保证是正正方形,我们只需要保证它不超过边界。

设该正方形距离左边界的距离为l,距右边界距离为r,距上边界距离为u,距下边界距离为d。

于是十分愉快一行取min走起:$sum1+=min(l,u),sum+=min(l,d),sum+=min(r,u),sum+=min(r,d)$;

接下来考虑坏点位于四个边上。

颓了一些性质(自己至今没有证上来……心里发虚)

我们只考虑左边界、右边界和上边界的关系来统计,其他直接旋转推广。

设该坏点距左边界的距离为x,距右边界的距离为y,距上边界的距离为h,

则有:

如果h<=min(x,y),对答案的贡献为h*(h+1)/2%mod;

如果min(x,y)<h<=max(x,y),对答案的贡献为min(x,y)*(min(x,y)+2)+min(x,y)*(h-min(x,y))

如果max(x,y)<h<=x+y,对答案的贡献为

min(x,y)*(min(x,y)+1)/2+min(x,y)*(max(x,y)-min(x,y))+(min(x,y)-1+min(x,y)-h+max(x,y))*(h-max(x,y))/2

如果h>x+y,对答案的贡献为x*y。

推广到四个方向,分别将左右上边界附成(l,r,u)(l,r,d)(u,d,l)(u,d,r)分别计算贡献即可。

处理sum2:

处理$sum2$的时候需要注意,我们处理出来的sum2是至少包含2个坏点的正方形总数。所以无需保证正方形的另外两个点不是坏点(我因为没想透卡了一小会儿)

所以我们只需要枚举两个坏点作为正方形的两个顶点,然后讨论这两个坏点作为正方形的边上的顶点和对角线上的顶点两种情况,讨论另外两个点是否在界内以及是否整点来统计答案。

作为边:1.另外两个点是$(A+D-B,B+A-C)(C+D-B,D+A-C)$

    2.另一方向的$(A+B-D,B+C-A)(C+B-D,D+C-A)$。

作为对角线,(利用全等三角形解方程可得):另外两个点是$((A-D+B+C)/2,B+C-(A-D+B+C)/2)((A-B+D+C)/2,A+D-(A-B+D+C)/2)$。

处理sum3、sum4:

这个显然和sum2放到一起就可以了。

网上的题解基本上一笔带过。然而细节还是需要处理一下的。

首先是判定问题。我最开始打了个暴搜判定,枚举每一个坏点坐标,匹配上了就返回1,否则一直搜到末尾返回0。

T到了2000ms。(真的飞起来了欸!)

然后学题解打hash链表,超快的说。783ms解决问题。

然而我sb的把模数设到了1e8+7,觉得可以借这个题给模数省一个define……

然后他就给了我一堆美妙的re。最开始re30,把数组压到1e7是re40,我意识到事情不太对,赶紧重开模数给它削了俩0,A了……

(好长啊 /仰望,给个推荐呗~)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#define mod 100000007
#define ll long long
#define int long long
#define K 1000007
using namespace std;
int n,m,k;
int sum0,sum1,sum2,sum3,sum4;
struct node{int x,y;}wor[];
struct hsss{int key,nxt;}data[];
int num,h[];
//bool iswor[3003][3003];
inline int query(int x,int y)
{
for(register int i=;i<=k;++i)
if(wor[i].x==x&&wor[i].y==y)
return ;
return ;
}
inline void ins(int key)
{
int x=key%K;
for(int i=h[x];i;i=data[i].nxt)
if(data[i].key==key)return;
data[++num].key=key;data[num].nxt=h[x];h[x]=num;
}
inline int cal(int x,int y,int h)
{
h-=;
if(x>y)swap(x,y);
if(h<=x)return (ll)h*(h+)/%mod;
if(h<=y)return ((ll)x*(x+)/+(ll)x*(h-x))%mod;
if(h>=x+y)return (ll)x*y%mod;
return ((ll)x*(x+)/+(ll)x*(y-x)+(ll)(x-+x-h+y)*(h-y)/)%mod;
}
/*inline bool query(int key)
{
int x=key%K;
for(int i=h[x];i;i=data[i].nxt)
if(data[i].key==key) return 1;
return 0;
}*/
inline void check(int x1,int y1,int x2,int y2)
{
if(x1<||x1>n||x2<||x2>n||y1<||y1>m||y2<||y2>m)return;
int res=query(x1,y1)+query(x2,y2);
sum2+=;if(sum2>=mod)sum2-=mod;
sum3+=res;if(sum3>=mod)sum3-=mod;
sum4+=(res==);if(sum4>=mod)sum4-=mod;
}
signed main()
{
scanf("%lld %lld %lld",&n,&m,&k);
for(register int i=;i<=k;++i){scanf("%lld %lld",&wor[i].x,&wor[i].y);ins(wor[i].x*(m+)+wor[i].y);}
for(register int i=;i<=min(n,m);++i){sum0+=i*(n-i+)*(m-i+)%mod;sum0%=mod;}
for(register int i=;i<=k;++i)
{
int l=wor[i].x,r=n-wor[i].x,u=wor[i].y,d=m-wor[i].y;
sum1+=min(l,u)+min(l,d)+min(r,u)+min(r,d);
if(sum1>=mod)sum1-=mod;
sum1+=cal(l,r,u);if(sum1>=mod)sum1-=mod;
sum1+=cal(l,r,d);if(sum1>=mod)sum1-=mod;
sum1+=cal(u,d,l);if(sum1>=mod)sum1-=mod;
sum1+=cal(u,d,r);if(sum1>=mod)sum1-=mod;
/*
int l=wor[i].x,r=m-wor[i].x,u=wor[i].y,d=n-wor[i].y;
int mn=min(l,r),mx=max(l,r);
if(u<=mn)sum1+=(u+3)*u/2;
else if(u>mn&&u<=mx)sum1+=(mn+3)*mn/2+(u-mn)*(mn+1);
else if(u>=mx+mn+1)sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn+1)*mn/2;
else sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn*2+1-u+mx)*(u-mx)/2;
if(d<=mn)sum1+=(d+3)*d/2;
else if(d>mn&&d<=mx)sum1+=(mn+3)*mn/2+(d-mn)*(mn+1);
else if(d>=mx+mn+1)sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn+1)*mn/2;
else sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn*2+1-d+mx)*(d-mx)/2;
mn=min(u,d),mx=max(u,d);
if(l<=mn)sum1+=(l+3)*l/2;
else if(l>mn&&l<=mx)sum1+=(mn+3)*mn/2+(l-mn)*(mn+1);
else if(l>=mx+mn+1)sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn+1)*mn/2;
else sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn*2+1-l+mx)*(l-mx)/2;
if(r<=mn)sum1+=(r+3)*r/2;
else if(r>mn&&r<=mx)sum1+=(mn+3)*mn/2+(r-mn)*(mn+1);
else if(r>=mx+mn+1)sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn+1)*mn/2;
else sum1+=(mn+3)*mn/2+(mx-mn)*(mn+1)+(mn*2+1-r+mx)*(r-mx)/2;
*/
}
for(register int i=;i<k;++i)
{
for(register int j=i+;j<=k;++j)
{
int x1=wor[i].x,y1=wor[i].y,x2=wor[j].x,y2=wor[j].y;
check(x1+y2-y1,y1+x1-x2,x2+y2-y1,y2+x1-x2);
check(x1+y1-y2,y1+x2-x1,x2+y1-y2,y2+x2-x1);
if((x1+x2+y1+y2)&) continue;
check(x1+x2+y2-y1>>,y1+y2+x1-x2>>,x1+x2+y1-y2>>,y1+y2+x2-x1>>);
/*
if(A+D-B>0||B+A-C>0||A+D-B<m||B+A-C<n)
if(C+D-B>0||C+D-B<m||D+A-C>0||D+A-C<n)
{
sum2++;
if(query(A+D-B,B+A-C)||query(C+D-B,D+A-C))sum3++;
if(query(A+D-B,B+A-C)&&query(C+D-B,D+A-C))sum4++;
}
if((A-D+B+C)%2)continue;if((A-D+B+C)%2)continue;
if((A-B+D+C)%2)continue;if((A-B+D+C)%2)continue;
if((A-D+B+C)/2>0&&(A-D+B+C)/2<m&&B+C-(A-D+B+C)/2>0&&B+C-(A-D+B+C)/2<n)
if((A-B+D+C)/2>0&&(A-B+D+C)/2<m&&A+D-(A-B+D+C)/2>0&&A+D-(A-B+D+C)/2<n)
{
sum2++;
if(query((A-D+B+C)/2,B+C-(A-D+B+C)/2)||query((A-B+D+C)/2,A+D-(A-B+D+C)/2))sum3++;
if(query((A-D+B+C)/2,B+C-(A-D+B+C)/2)&&query((A-B+D+C)/2,A+D-(A-B+D+C)/2))sum4++;
}
*/
}
}
// cout<<sum0<<" "<<sum1<<" "<<sum2<<" "<<sum3<<" "<<sum4<<endl;
cout<<((sum0-sum1+sum2-sum3/+sum4/)%mod+mod)%mod<<endl;
return ;
}

ps:注释掉的是我的调试和重构代码前的代码……写崩了……

「题解」:[BZOJ4558]方的更多相关文章

  1. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  2. 「题解」「HNOI2013」切糕

    文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...

  3. 「题解」JOIOI 王国

    「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...

  4. 「题解」:[loj2763][JOI2013]现代豪宅

    问题 A: 现代豪宅 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...

  5. 「题解」:07.16NOIP模拟T2:通讯

    问题 B: 通讯 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 “这一切都是命运石之门的选择.” 试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短 信,并由此 ...

  6. 「题解」:$Six$

    问题 A: Six 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...

  7. 「题解」:$Smooth$

    问题 A: Smooth 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...

  8. 「题解」:Kill

    问题 A: Kill 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...

  9. 「题解」:y

    问题 B: y 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...

随机推荐

  1. uoj139 【UER #4】被删除的黑白树

    题目 不难发现有一个暴力\(dp\) 设\(dp[x][l]\)表示\(x\)点子树内所有叶子节点到\(x\)的路径上都有\(l\)和黑点时最多能染多个黑点 转移就是 \[dp[x][l]=\max( ...

  2. Thread-per-Message 这个工作交给你了

    Per是“每一”的意思,所以thread per message解释过来就是“每个消息一个线程”,message在这里可以看做是“命令”或“请求”的意思,对每隔命令或请求,分配一个线程,有这个线程执行 ...

  3. Activiti学习笔记7 — ReceiveTask机器自动任务的使用

    一. 创建流程规则,如下图 二.发布流程 /** * 2.发布一个流程 */ @Test public void testDeployProcess() { RepositoryService rep ...

  4. Unity开发一些实用的提高效率的技巧

    该文章参考总结自Unity微信官方 原文: Unity小技巧介绍 1 如果编辑器意外崩溃了,但场景未保存,这时可以打开工程目录,找到/Temp/_Backupscenes/文件夹,可以看到有后缀名为. ...

  5. css---7自定义字体

    1.Adobe illustrator AI是一种应用于出版.多媒体和在线图像的工业标准矢量插画的软件,是一款非常好的矢量图形处理工具. 该软件主要应用于印刷出版.海报书籍排版.专业插画.多媒体图像处 ...

  6. Java int和Integer包装类的区别和比较

    区别:                                                                                                  ...

  7. leetcood学习笔记-45-跳跃游戏二

    题目描述: 第一次提交;超时 class Solution: def jump(self, nums: List[int]) -> int: l = [] for i in range(len( ...

  8. JavaScript - 判断当前使用的浏览器类型

    <script> window.onload = function() { // 判断当前使用的浏览器类型 var browserType = navigator.userAgent.to ...

  9. git使用过程中问题

    git提交文件命令顺序 fetch merge add commit pull push 取消已add文件 git reset HEAD 文件名 覆盖本地文件 git checkout 文件名 $ g ...

  10. BZOJ 1010 (HNOI 2008) 玩具装箱

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MB Submit: 12665 Solved: 5540 [Submit][S ...