BZOJ 3505 浅谈组合数学在图形区域分割问题的应用

这里写图片描述
世界真的很大
在数论中,组合数和欧拉函数一样是个神奇的东西
如果说凡是看到gcd就要想到欧拉的话
那凡是看到“选几个”就应该往组合数方面想了
这种代码简单的玩意儿
难度全在于如何应用,理解的不够好,或者建模方法有问题
都是可能能卡很久的,尤其是代码量真的很短的情况下
就更悲催了
比如我就已经看了一整个晚上都没有想出来
最后还得大神指导才想到了正确的建模方法
哎。。还是不行啊。。233
还是先看题
description

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。

注意三角形的三点不能共线。

input

输入一行,包含两个空格分隔的正整数m和n。

output

输出一个正整数,为所求三角形数量。

首先应该想到的是组合数
即求在m*n个点里面选3个点的组合数
但是这样是有毒的
所选的3个点有可能在一条直线上,而这种情况是不能组成三角形的
我们需要排除这样的情况
首先是三个点在同一列(行)的情况,直接算出行列,3的组合数再乘以有多少行和有多少列就好
还有就是三个点在同一斜边的情况,这个的处理就比较复杂了
考虑n^2枚举这样的斜边,再算出每条斜边3的组合数累加,整个矩阵的对角线特判一下
这样是不行的
首先会算漏。因为我们枚举的是边都在大矩形的边上的矩形的斜边,边都是整点
但有些斜边落在大矩形的边上的点并不是整点
于是考虑平移这样的斜边
这样理论上来讲是可以的,但是要考虑边长是gcd(边长)的几倍。方程化出来和下面的方法的方程是一样的
换一个角度考虑,不要枚举斜边了,直接考虑枚举在一条直线上的3个点吧,这样就不用考虑重复枚举的bug了,因为枚举的3个点肯定是不一样的。
那就考虑枚举3个点的两个端点,再用gcd算出两个端点之间有多少个整点,就有在端点确定情况下的方案数
但是呢,因为有平移的缘故,我们干脆就确定一个端点为(0,0),只枚举另一个端点的位置,整张图内能平移得到的个数就是(n-i)*(m-j)
完整代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long dnt;
dnt n,m;

dnt com(dnt x)
{
    if(x<3) return 0;
    return x*(x-1)*(x-2)/6;
}

dnt gcd(dnt a,dnt b)
{
    return b==0 ? a : gcd(b,a%b) ;
}

dnt sov()
{
    dnt rt=0;
    for(int i=1;i<n;i++)
        for(int j=1;j<m;j++)
            rt+=(gcd(i,j)-1)*(n-i)*(m-j);
    return (rt*2);
}

int main()
{
    scanf("%lld%lld",&n,&m);
    n++,m++;
    printf("%lld",com(m*n)-n*com(m)-m*com(n)-sov());
    return 0;
}

嗯,就是这样

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值