• HDU 4483 HDOJ Lattice triangle


    http://acm.hdu.edu.cn/showproblem.php?pid=4483

    题目意思:

      给定一个n*n的广场,问选取三个整点构成三角形的方案数。

    今天做hust的练习赛的时候,碰到了UVALive3295,是求n*m的矩阵上的方案数。不过那题n,m没这题大。然后就讲到这题,就做了一下,还是很神奇的一题。

    整体思路:总方案数 - 三点成线方案数。

    n*n的矩阵,整点数为(n+1)*(n+1),以下讨论,直接另n = n+1

    总方案数C(n*n, 3)

    三点构成的线与坐标轴平行,C(n, 3)*n*2

    本题重点就在求三点成斜线的方案数。

    我们来这么考虑三个点,让三个点的横坐标依次增大把旁边两个点的连线看做矩形的对角线,中间的点就在对角线上。

    易知矩形对角线有两条,我们不妨只计算对角线方向为右的方案数,*2就是总方案数。

    长为i,宽为j的矩形,对角线上的整点数为gcd(i, j)-1,在正方形中有(n-i)*(n-j)个

    于是,让tmp表示三点成右斜线的方案数,有以下代码

    tmp=0;
    for(int i=2;i<n;i++)
    for(int j=2;j<n;j++)
        tmp+=(n-i)*(n-j)*(__gcd(i,j)-1);

    这样复杂度为n^2,还不能满足题目要求。不过我们可以在此基础上思考。

    我们反过来枚举gcd的值呢?

    假设i, j的gcd为k

    那么gcd(i/k, j/k)==1,且 i, j<n

    令a=i/k, b=j/k

    则a,b的pair数 = 1 + 2*(ph(2) + ph(3) + ph(4) + ... + ph((n-1)/k))         ph()为欧拉函数

    最前面的1表示 a=1,b=1

    其他情况下a==b,a,b是不互质的。

    令a<b,枚举a,b的取值就是ph(a),当然,a, b顺序可以换,所以要最前面要*2

    而这么多的a,b计算

       sum((n-i)*(n-j)*(__gcd(i, j)-1))

    = sum((n - k*a) * (n - k*b) * (k-1))

    = (k-1)*sum(n^2 - n*k*(a+b) + k^2*a*b)

    n^2的系数就是a,b的pair数

    n*k的系数就是这么多对a,b的a+b的总和

    k^2的系数就是这么多对a,b的a*b的总和

    插播一段数论

      对于n>2, 若存在一个m,使得gcd(n,m)==1,则gcd(n, n-m)==1。反证法可证。所以可以理解为和n互素的数总是成对出现,和为n。

      所以[1, n]中与n互质的数的总和为ph(n)*n/2

      而对于n>2的偶数,gcd(n/2, n)!=1,不用讨论。n==2时,总和为1,恰好也满足这个式子。

    于是上面三个系数,都可以通过欧拉函数求出。

    本题解决。

    代码如下:

     1 #include<cstdio>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=100005;
     5 const ll md=1000000007,md2=md*6;
     6 int n,ph[N],sc[N],ss[N],sm[N];
     7 void gph(){
     8     ph[1]=sc[1]=sm[1]=1;
     9     ss[1]=2;
    10     for(int i=2;i<N;i++)if(!ph[i])
    11         for(int j=i;j<N;j+=i){
    12             if (!ph[j]) ph[j]=j;
    13             ph[j]-=ph[j]/i;
    14         }
    15     for(int i=2;i<N;i++){
    16         sc[i]=(sc[i-1]+(ll)ph[i]*2)%md;
    17         ss[i]=(ss[i-1]+(ll)i*ph[i]*3)%md;
    18         sm[i]=(sm[i-1]+(ll)i*ph[i]%md*i)%md;
    19     }
    20 }
    21 int main()
    22 {
    23     gph();
    24     int T;
    25     scanf("%d",&T);
    26     while(T--){
    27         scanf("%d",&n);n++;
    28         ll tmp=(ll)n*n%md,ans;
    29         ans=tmp*(tmp-1)%md2*(tmp-2)%md2/6;
    30         ans-=tmp*(n-1)%md2*(n-2)*2%md2/6;
    31         tmp=0;
    32         for(int k=2;k<n;k++){
    33             int m=(n-1)/k;
    34             tmp=(tmp+(k-1)*((ll)sc[m]*n%md*n%md - (ll)n*k%md*ss[m]%md + (ll)k*k%md*sm[m]%md))%md;
    35         }
    36         ans-=tmp*2;
    37         printf("%I64d\n",(ans%md+md)%md);
    38     }
    39     return 0;
    40 }
  • 相关阅读:
    前端学PHP之错误处理
    mysql数据库学习目录
    前端学数据库之存储
    前端学数据库之函数
    用shell脚本监控进程是否存在 不存在则启动的实例
    在notepad++里面使用正则表达式替换掉所有行逗号前面内容
    mysql合并 两个count语句一次性输出结果的方法
    硬件中断和DPC一直占40-52%左右 解决方法
    解决secureCRT 数据库里没有找到防火墙 '无' 此会话降尝试不通过防火墙进行连接。
    Java eclipse下 Ant build.xml实例详解 附完整项目源码
  • 原文地址:https://www.cnblogs.com/hundundm/p/2908153.html
Copyright © 2020-2023  润新知