• bzoj3505 [Cqoi2014]数三角形


    Description

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

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

    Input

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

    Output


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

    Sample Input


    2 2

    Sample Output

    76


    数据范围
    1<=m,n<=1000

    一开始不会做……orz了各路神犇之后才有了思路

    首先答案就是所有取出三个点的方案数减去会三点共线的方案数

    显然n*m的网格上有(n+1)*(m+1)个整点,然后令t=(n+1)*(m+1),那么取三个点的方案数就是t*(t-1)*(t-2)/6(就是排列组合啦T T)

    然后要考虑怎么算三点共线的方案数

    然后有一个结论是在(a,b) (x,y)两点构成的线段上有gcd(a-x,b-y)-1个整点(a>x,b>y)

    我们固定(a,b) (x,y)为共线的三点中左边两个,那么第三个点的方案数就是gcd(a-x,b-y)-1

    但是这样枚举abxy的复杂度是O(n^2*m^2)

    优化是把这线段平移到原点处,那么会发现其实只要枚举(0,0) (x-a+1,y-b+1),其他的线段平移就可以了(这里强烈建议自己动手画一画!

    画完很容易发现这样(0,0) (x-a+1,y-b+1)的线段可以平移出(n-i+1)*(m-j+1)种不同方案,在 (x-a+1,y-b+1)的时候如果不在坐标轴上还要算两次

    另外,先打表算gcd竟然能这么快……

    #include<cstdio>
    #define LL long long
    int gcd[1010][1010];
    int n,m;
    LL t,ans;
    inline int getgcd(int a,int b)
    {
    	if (gcd[a][b])return gcd[a][b];
    	if (!a)return gcd[a][b]=b;
    	if (!b)return gcd[a][b]=a;
    	return gcd[a][b]=getgcd(b,a%b);
    }
    inline void calc()
    {
    	for(int i=1;i<=m;i++)gcd[0][i]=i;
    	for(int i=1;i<=n;i++)gcd[i][0]=i;
    	for(int i=1;i<=n;i++)
    	  for(int j=1;j<=m;j++)
    	    getgcd(i,j);
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	calc();
    	t=(n+1)*(m+1);
    	ans=t*(t-1)*(t-2)/6;
    	for (int i=0;i<=n;i++)
    	  for (int j=0;j<=m;j++)
    	    if (i||j)
    	    {
    	    	if (!i||!j)ans-=(LL)(gcd[i][j]-1)*(n-i+1)*(m-j+1);
    	    	else ans-=(LL)2*(gcd[i][j]-1)*(n-i+1)*(m-j+1);
    	    }
    	printf("%lld",ans);
    }
    

      

    ——by zhber,转载请注明来源
  • 相关阅读:
    Atom 和 markdown 基本使用
    c++11 正则表达式基本使用
    Emacs 之窗口管理
    Emacs 之列编辑模式
    Emacs 之查看帮助
    使用 json_in_java
    Linux服务 httpd
    Linux 编译安装BIND
    Kerberos
    Linux服务 DNS&BIND
  • 原文地址:https://www.cnblogs.com/zhber/p/4035928.html
Copyright © 2020-2023  润新知