您的位置:首页 > 其它

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

2017-07-06 09:51 381 查看


世界真的很大

在数论中,组合数和欧拉函数一样是个神奇的东西

如果说凡是看到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;
}


嗯,就是这样
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: