哈尔滨理工大学2016新生赛F题

给出两个正整数m,n,在笛卡尔坐标系中选出四个不同的点,满足:

(1)   点的横坐标是一个在区间[0,m]的整数。

(2)   点的纵坐标是一个在区间[0,n]的整数。

(3)   这四个点做顶点构成一个菱形。

有多少种满足以上条件的选择方法呢?

Input

多组测试数据,每组输入两个正整数m,n(m <= 1000, n <= 1000)。

处理到文件结束。

Output

每行输出一个整数,表示有多少满足条件的选择方法。

Sample Input

2 2

Sample Output

6

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const int maxn=1001;    //矩形最大边长

int f[maxn][maxn];        //以[0,m]×[0,n]为包含菱形的最小矩形的菱形的个数
long long g[maxn][maxn];//在[0,m]×[0,n]以m,n为边界的菱形的个数
long long h[maxn][maxn];//在[0,m]×[0,n]中菱形的个数
//扩展欧基里德算法,返回结果为最大公约数d,且ax+by=d
int egcd(int a,int b,long long &x,long long &y)
{
    long long k;            //临时变量
    int d;                    //最大公约数
    if (b==0)                //终止条件
    {
        x=1;                //满足终止条件时x的值
        y=0;                 //满足终止条件时y的值
        return a;            //最大公约数为a
    }
    else
    {
        d=egcd(b,a%b,x,y);    //递归求解
        k=a/b;
        k=x-k*y;                //临时变量用于交换两个数
        x=y;                    //扩展欧基里德算法中,从上一层得到x=y
        y=k;                    //扩展欧基里德算法中,从上一层得到y=x-(a/b)*y
        return d;                //最大公约数为递归求解结果
    }
}
//计算区间的上下界
void cal_bound(long long x,long long step,long long &l,long long &r,int lb,int rb)
{
    int temp;
    if (step<0)                //当步长为负数时,进行镜像调整使得步长为正
    {
        x=-x;                    //x取相反数
        step=-step;            //步长取相反数
        temp=lb;
        lb=-rb;
        rb=-temp;                //把左右边界取相反数并且交换
    }
    //求最小的l使x+l*step>=lb
    if (lb-x>=0)                //左边界在已知解的右边
        l=(lb-x+step-1)/step;
    else                        //左边界在已知解的左边
        l=(lb-x)/step;
    //求最大的r使x+r*step<=rb
    if (rb-x>=0)                 //右边界在已知解的右边
        r=(rb-x)/step;
    else                        //右边界在已知解的左边
        r=(rb-x-step+1)/step;
    return;
}
//求ax+by=c在lx<=x<=rx且ly<=y<=ry时整数解的个数
int cal(int a,int b,int c,int lx,int rx,int ly,int ry)
{
    long long x,y,dx,dy,l1,r1,l2,r2;
    int d;
    d=egcd(abs(a),abs(b),x,y);    //使用扩展欧基里德算法
    if (c%d!=0)                    //不存在解的情况
        return 0;
    if (a<0)                        //如果a为负数,则相应调整x
        x=-x;
    if (b<0)                         //如果b为负数,则相应调整y
        y=-y;
    x*=c/d;                        //求出其中一个解的x值
    y*=c/d;                        //求出其中一个解的y值
    dx=b/d;                        //x的变化步长
    dy=-a/d;                        //y的变化步长
    cal_bound(x,dx,l1,r1,lx,rx);//通过x求t的左右边界
    cal_bound(y,dy,l2,r2,ly,ry);//通过y求t的左右边界
    if (l1<l2)                    //取左边界的最大值
        l1=l2;
    if (r1>r2)                    //取右边界的最小值
        r1=r2;
    return r1-l1+1;                //返回解的个数
}
int init()                        //预处理函数
{
    int i,j,temp;
    for(i=1;i<maxn;i++){        //枚举矩形的其中一边长
        for(j=1;j<maxn;j++){    //枚举矩形的另一边长
            temp=cal(2*i,-2*j,i*i-j*j,0,i,0,j);//计算情况(1)的结果
            if (i==j)                //当矩形为正方形时,正方形重复计算了一次
                temp--;
            if (temp>0)
                f[i][j]+=temp;    //将合法解累加到f数组中
            temp=cal(2*i,2*j,i*i+j*j,1,i-1,1,j-1);//计算情况(2)的结果
            if (i%2==0&&j%2==0)    //减去菱形面积为0的情况
                temp--;
            if (temp>0)
                f[i][j]+=temp;    //将合法解累加到f数组中
            //从f数组到g数组的转移方程
            g[i][j]=g[i-1][j]+g[i][j-1]-g[i-1][j-1]+f[i][j];
            //从g数组到h数组的转移方程
            h[i][j]=h[i-1][j]+h[i][j-1]-h[i-1][j-1]+g[i][j];
        }
    }
    return 0;
}

int main()
{
    int m,n;
    init();                                //预处理
    while(scanf("%d%d",&m,&n)!=EOF){    //输入整数m,n直到文件结束
        printf("%I64d\n",h[m][n]);        //输出答案
    }
    return 0;
}
时间: 2024-12-24 06:06:52

哈尔滨理工大学2016新生赛F题的相关文章

哈尔滨理工大学2016新生赛A题

一名骑着马的强盗闯进了原本平静祥和的棋盘村,为了通知村里的士兵来打败强盗,你必须要通知位于棋盘村最下方的兵营.棋盘村的地形就像是一张棋盘,你所在的位置为A点(0,0),兵营位于棋盘村的右下角B点(n,m).你每次只能走一步,可以选择向下走,也可以选择向右走.但是强盗所在的位置和强盗的马一次所能跳到的位置是不可以走过去的(强盗的马的移动方法与象棋中的马相同).请计算出从A点能够走到B点的所有路径条数. 首先输入一个整数t,代表有t组测试数据. 每组测试数据为四个整数,即B点的坐标(n,m)和强盗的

哈尔滨理工大学2016新生赛E题

多组数据,每组测试数据输入9个整数,为1-9的一个全排列.初始状态会被描述为 1 2 3 4 5 6 7 8 9 Output 输出所需要的最小移动步数. Sample Input 1 2 3 4 5 6 7 8 9 9 8 7 6 5 4 3 2 1 Sample Output 0 12 将每个排列利用康托展开压缩为一个整数,采用广度优先搜索的方式不停的搜索直到得到目标状态即可. #include <stdio.h> #include <string.h> const int M

哈尔滨理工大学2016新生赛G题

FBI Tree的描述如下: 我们可以把由0和1组成的字符串分为3类,全0的串成为B串,全1的串成为I串,既含0又含1的串则称为F串.FBI树是一种二叉树,它的节点类型也包括F串节点.B串节点和I串节点三种.由一个 长度为2^N的01串S可以构造出一颗FBI树T,递归的构造方法如下: (1)   T的根节点为R,其类型与串S的类型相同. (2)   若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2:由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2. 现在给出一个长

哈尔滨理工大学2016新生赛B题

做为长城上的卫士,影踪派一直守在螳螂高原上防止螳螂人卡拉克西的入侵.影踪派的长城可以近似看做是一条直线上依次编号为1~N的N个基地组成,编号相邻的两个基地之间由长城相连接,而影踪派掌门祝踏岚所在的影踪禅院位于编号为1的基地.     祝踏岚发现,每次螳螂人只会选择长城上的一个基地进行猛烈的攻击,所以,祝踏岚每次都要从影踪禅院赶到被攻击的基地亲临前线指挥战斗.但是,在长城上移动是很耗时的,为了能更快的赶到任何一个可能被攻击的基地,祝踏岚决定修建一对传送门.     一对传送门由两个入口组成,它能实

哈尔滨理工大学2016新生赛D题

陈月亮从小就热爱数学,这天老师讲到任何一个正整数N,我们可以很容易的找出N的所有因子,N1,N2,N3...,Nk,称N一共有k个因子(包含1和N本身). 求出k的值这个问题对于陈月亮来说实在是太简单了,于是她想要求出N所有因子的因子个数(如N1可能包含n1个因子(包含1和N1本身),N2可能包含n2个因子,...,Nk可能包含nk个因子),然后计算出S的值: 第一行为一个整数T(T <= 10000),代表测试数据的组数. 接下来T行每行一个正整数N(N < 2 ^ 31). 对于每组测试数

哈尔滨理工大学2016新生赛H题

陈月亮最喜欢的季节就是冬天了,这不看着窗外飘起了雪花,陈月亮开心的跑出屋来看雪.但是迷迷糊糊的陈月亮不知道自己是在做梦还是真的下起了雪.突然她想起了一句话,在真实世界中是没有两片一样的雪花的.于是你的任务就是比较这场雪中的所有雪花,如果出现了两朵完全一致的雪花,则证明陈月亮是在梦中. 每朵雪花用六个整数表示,范围在(1 – 10000000)之间,表示雪花六个花瓣的长度,六个整数的先后出现顺序可能是顺时针顺序也可能是逆时针顺序,并且可能是从任意一个花瓣开始的.比如说对同一个花瓣,描述方法可能是1

哈尔滨理工大学2016新生赛C题

一个r行c列的矩阵里的所有元素都为0或1,给出这个矩阵每一行的和以及每一列的和,那么是否存在这样一个矩阵满足条件呢,如果存在任意一个满足条件的矩阵则输出YES,如果不存在则输出NO? 每组测试数据第一行包含两个整数r,c,表示矩阵的行数和列数. 第二行包含r个32位无符号数,表示矩阵每行的和. 第三行包含c个32位无符号数,表示矩阵每列的和. (1 <= r,c <= 100000) 如果存在这样的一个01矩阵,输出YES,否则输出NO 首先需要判断行和列的总和是否相等,因为它们都应该是整个矩

哈尔滨理工大学2016新生赛I题

这次我们要写一个简单的行编辑器,当按下'#'时代表按下了一次退格符,当按下'@'时代表一个退行符(使当前行的字符全部无效).例如,假设从终端接收了这样的两行字符: Whil#lr#e(s#*s) [email protected](*s=#++) 则实际有效的是下列两行: While(*s) putchar(*s++) 请你编写一个程序,输出实际有效的字符串. 第一行是一个整数T,表示测试数据组数. 接下来每行为一个字符串(不含空格和任何空白),表示输入的原始字符串 输出最终的正确字符串. 2

哈尔滨理工大学2016新生赛K题

小明这个人特别无聊,他喜欢把一个字符串以一个奇怪的姿势压缩起来.他会把连续的字符表示成字符和数字的形式,而且他用的数字还都是一位的!!!比如下面这个例子:"a12" 表示的是 "aaaa".也就是说压缩后的字符串里的每个数字表示重复最后一个出现的字符数字次,就好像上面的例子. 现在给你一些被无聊的小明压缩过的字符串,你能还原它么? 输入的第一行包括一个整数 T,表示数据的组数. 每组输入包括一行,一个字符串,表示被小明压缩过的字符串.长度不超过50. 每组输出包括一