4558: [JLoi2016]方

4558: [JLoi2016]方

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 352   Solved: 156
[ Submit][ Status][ Discuss]

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

[ Submit][ Status][ Discuss]



#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
#include<map>
using namespace std;
 
const int maxn = 2020;
typedef long long LL;
const LL mo = 100000007;
 
struct Point{
    int x,y; Point(){}
    Point(int x,int y): x(x),y(y){}
    bool operator < (const Point &B) const
    {
        if (x < B.x) return 1;
        if (x > B.x) return 0;
        return y < B.y;
    }
    Point operator - (const Point &B) const {return Point(x - B.x,y - B.y);}
    Point operator + (const Point &B) const {return Point(x + B.x,y + B.y);}
}p[maxn];
typedef Point Vector;
 
int n,m,k,Ans,s1,s2,s3,s4;
 
map <Point,bool> M;
 
inline int Mul(const LL &x,const LL &y) {return x * y % mo;}
inline int min(const int &x,const int &y) {return x < y ? x : y;}
inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}
inline int Dec(const int &x,const int &y) {return x - y >= 0 ? x - y : x - y + mo;}
 
void Check(Point p1,Point p2)
{
    if (p1.x < 0 || p1.x > n || p1.y < 0 || p1.y > m) return;
    if (p2.x < 0 || p2.x > n || p2.y < 0 || p2.y > m) return;
    int cnt = 0; ++s2;
    if (M.count(p1)) ++cnt; if (M.count(p2)) ++cnt;
    if (cnt == 1) ++s3; else if (cnt == 2) ++s4;
}
 
inline int Calc1(int pos,int res,int N)
{
    int ret = 0,now = min(pos,min(res,N - pos));
    ret = 1LL * now * (now - 1) / 2LL % mo;
    if (now == res) return ret;
    int a = pos,b = N - pos; if (a > b) swap(a,b);
    b = min(b,res); ret = Add(ret,Mul(b - a,a));
    if (b == res) return ret; a = min(res,N);
    now = N - (b + 1) + 1; a = a - b;
    int g = 1LL * (now + now - a + 1) * a / 2LL % mo;
    return Add(ret,g);
}
 
inline int Calc2(int pos,int res,int N)
{
    int ret = 0,now = min(pos,min(res,N - pos));
    ret = 1LL * now * (now + 3) / 2LL % mo;
    if (now == res) return ret;
    int a = pos,b = N - pos; if (a > b) swap(a,b);
    b = min(b,res); ret = Add(ret,Mul(b - a,a + 1));
    if (b == res) return ret; a = min(res,N);
    now = N - (b + 1) + 1; a = a - b;
    int g = 1LL * (now + now - a + 1) * a / 2LL % mo;
    return Add(ret,g);
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    cin >> n >> m >> k;
    for (int i = 1; i <= min(n,m); i++)
        Ans = Add(Ans,Mul(Mul(n - i + 1,m - i + 1),i));
    for (int i = 1; i <= k; i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        s1 = Add(s1,Calc1(x,y,n));
        s1 = Add(s1,Calc1(x,m - y,n));
        s1 = Add(s1,Calc2(y,x,m));
        s1 = Add(s1,Calc2(y,n - x,m));
        p[i] = Point(x,y); M[Point(x,y)] = 1;
    }
    for (int i = 1; i < k; i++)
        for (int j = i + 1; j <= k; j++)
        {
            Vector v = p[j] - p[i]; swap(v.x,v.y);
            v.y *= -1; Check(p[i] + v,p[j] + v);
            v.x *= -1; v.y *= -1; Check(p[i] + v,p[j] + v);
            v = p[j] - p[i]; if ((v.x + v.y) & 1) continue;
            Vector g = Point((v.x + v.y) / 2,(v.y - v.x) / 2);
            Vector h = Point((v.x - v.y) / 2,(v.y + v.x) / 2);
            Check(p[i] + g,p[i] + h);
        }
    s3 /= 3; s4 /= 6; s3 += s4 * 4;
    cout << ((Ans - s1 + s2 - s3 + s4) % mo + mo) % mo << endl;
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值