UVALive - 3720 Highways 组合计数

题意:

给n*m的点阵,问有多少条线通过至少两个点,且不竖直,不水平。


题解:

       首先,由于\这个斜线的数目和/的数目是相同的,所以我们只需计算\的数目然后×2即可。

      用d[i][j]保存从左上角(0,0)到(i,j)这个矩形中从(0,0)出发的线总数,可以发现当gcd(i,j)==1时,从(0,0)到(i,j)这条直线在该矩形中没有重复。

       所以d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(gcd(i,j)==1);

       那么我们求出左上角(0,0)到(i,j)这个矩形内从(0,0)出发的线数有什么用呢?

       接下来我们用pre[i][j]来保存从(0,0)到(i,j)这个矩形中所有的可行线数目,我们发现每多一个新的点(i,j),pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+(经过(i,j)且在(0,0)到(i,j)这个矩形内的可行线数目)。

       那么这个数目怎么求呢?我们先前d[i][j]保存的是从(0,0)出发,其实从(0,0)出发等价于从(i,j)出发,因为矩形相同。

       所以pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+d[i][j]。。?其实不是,还有重复,什么情况会重复呢?从(i,j)出发的线是否在之前就已经算在之内了呢?

       我们可以发现:线重复的充要条件是从(i,j)出发的线至少经过了该矩形中另外两个点

       那么怎么去掉呢?

       我们发现这种线的数目必定是在d[i/2][j/2]里面,因为线的长度起码要乘2才会经过另外一个点,并且d[i/2][j/2]中所有的线都会重复,因为里面线的长度都起码可以×2,而只要长度×一个大于1的数,他必定可以经过另外一个点。而先前gcd(i,j)==1这个条件,是让线在d[i][j]中没有重复。

所以最终pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+d[i][j]-d[i/2][j/2];



#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
#include <stack>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <iomanip>
#include <sstream>
#include <ctime>
#define dep(a,b,c) for(int a=b;a>=c;a--)
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define mem(a,b) memset(a,b,sizeof(a))
#define IO ios::sync_with_stdio(false)
#define pb push_back
#define debug(x) cout<<"["<<x<<"]"<<endl
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1e9+7;
const int MAXN= 3e2+5;
int d[MAXN][MAXN];
int pre[MAXN][MAXN];
int main() {
    int n,m;
    rep(i,1,300)
        rep(j,1,300)
            d[i][j]=d[i-1][j]+d[i][j-1]-d[i-1][j-1]+(__gcd(i,j)==1);
    rep(i,1,300)
        rep(j,1,300)
            pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+d[i][j]-d[i/2][j/2];
    while(~scanf("%d%d",&n,&m)){
        if(!n&&!m)break;
        printf("%d\n",pre[n-1][m-1]*2);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值