Kickstart Round A 2017 Problem A. Square Counting 公式、数论逆元、除法取模

如果

Problem A. Square Counting

This contest is open for practice. You can try every problem as many times as you like, though we won't keep track of which problems you solve. Read the Quick-Start Guide to get started.
Small input
8 points
Solve A-small
Large input
17 points
Solve A-large

Problem

Mr. Panda has recently fallen in love with a new game called Square Off, in which players compete to find as many different squares as possible on an evenly spaced rectangular grid of dots. To find a square, a player must identify four dots that form the vertices of a square. Each side of the square must have the same length, of course, but it does not matter what that length is, and the square does not necessarily need to be aligned with the axes of the grid. The player earns one point for every different square found in this way. Two squares are different if and only if their sets of four dots are different.

Mr. Panda has just been given a grid with R rows and C columns of dots. How many different squares can he find in this grid? Since the number might be very large, please output the answer modulo 109 + 7 (1000000007).

Input

The first line of the input gives the number of test cases, TT lines follow. Each line has two integers R and C: the number of dots in each row and column of the grid, respectively.

Output

For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the number of different squares can be found in the grid.

Limits

1 ≤ T ≤ 100.

Small dataset

2 ≤ R ≤ 1000.
2 ≤ C ≤ 1000.

Large dataset

2 ≤ R ≤ 109.
2 ≤ C ≤ 109.

Sample


Input 
 

Output 
 
4
2 4
3 4
4 4
1000 500

Case #1: 3
Case #2: 10
Case #3: 20
Case #4: 624937395

The pictures below illustrate the grids from the three sample cases and a valid square in the third sample case.






这题好难啊。。。我怎么这么垃圾啊。。。比赛当天large的数据根本算不出来,然后觉得google的大数据要一次通过。。。。好难啊。。

今天自己认真的写了一下。。因为数据范围是1e9,所以对复杂度要求很高的。。

写一写自己推导公式的过程吧,还是zyyyyy的代码好看,代码写的丑是我一直以来。。

题意是要求我们求出一个n*m的网格中有多少个正方形,具体看样例很快就能明白。

(一下讨论的n和m是指边长,题目中的n和m是指点,注意)

首先考虑非斜的正方形,在一个n*m(n<=m)的网格中,非斜的正方形的个数应该是:

Kickstart Round A 2017 Problem A. Square Counting 公式、数论逆元、除法取模_第1张图片

那么斜的正方形呢,可以看出,在每个边长为k网格的非斜正方形中,斜的正方形个数应该是k-1个

比如k=4,那么如下图(其实就是从一条边枚举正方形一个角的起点罢了):

Kickstart Round A 2017 Problem A. Square Counting 公式、数论逆元、除法取模_第2张图片

那么在n*m的网格中,斜正方形的个数如下:

Kickstart Round A 2017 Problem A. Square Counting 公式、数论逆元、除法取模_第3张图片

将非斜的和斜的加起来并展开:

Kickstart Round A 2017 Problem A. Square Counting 公式、数论逆元、除法取模_第4张图片

其中n(n+1)(2n+1)/6和[n(n+1)/2]^2分别是平方、立方求和公式。

这样看上去似乎对于每次询问复杂的可以降低O(1)

然而!!因为求解过程中处处都需要取模,在立方求和公式中存在着一个除法取模的问题。

关于这个问题,要利用逆元来达到以除法换乘法。在比赛中zyyyyy居然就能做出来一发过。。。实在厉害。。。

取模乘法换除法的方法可以参考这篇博客,我觉得写得很清楚,也是我最终代码实现的方法。

最后分析一下复杂度。。。应该是在那个快速幂上吧,所以是O(log mod)(如果错了还望指出)

下面代码,数论还是要好好学的啊。。。。。脑子不灵光

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const long long mod = 1e9+7;
long long n,m;
long long queen(long long x,long long another){
    if(another==1){
        return x;
    }
    long long res;
    res = queen(x, another/2)%mod;
    res = (res*res)%mod;
    if(another%2){
        res = (res*x)%mod;
    }
    return res;
}
int main(){
    freopen("A-large-practice.in","r",stdin);
    freopen("out.txt", "w", stdout);
    int t;
    scanf("%d",&t);
    int rnd = 1;
    while(t--){
        scanf("%lld%lld",&n,&m);
        if(n>m){
            swap(n,m);
        }
        n--,m--;
        long long k;
        long long res = 0;
        res = 0;
        long long now = (((n*m)%mod + (m+n+1)%mod)%mod);
        k = ((1+n)*n/2)%mod;
        now = (now * k)%mod;
        res = (res+now)%mod;
        now = (n+m+2)%mod;
        k = (((n*(n+1))%mod)*(2*n+1)%mod*queen(6,mod-2))%mod;
        res = (res - k*now)%mod;
        if(res<0){
            res += mod;
        }
        k = ( ((n*(n+1)/2)%mod) * ((n*(n+1)/2)%mod) )%mod;
        res = (res + k)%mod;
        printf("Case #%d: %lld\n",rnd++,res);
    }
    
    return 0;
}









你可能感兴趣的:(快速幂取模,数论)