zoukankan      html  css  js  c++  java
  • UVa 1393 (容斥原理、GCD) Highways

    题意:

    给出一个n行m列的点阵,求共有多少条非水平非竖直线至少经过其中两点。

    分析:

    首先说紫书上的思路,编程较简单且容易理解。由于对称性,所以只统计“”这种线型的,最后乘2即是答案。

    枚举斜线包围盒的大小,如果盒子的长宽ab互质,则是可以的。这种盒子共有(m-a)(n-b)个,但要减去其中重复的。如果有一个长宽为2a和2b的大盒子,则计数右下角的小盒子的同时,左上角的小盒子会重复,所以要减去重复的盒子的个数c = max(0, m-2a) * max(0, n-2b)

    最后gcd(a, b)的值是要预处理的

     1 #include <cstdio>
     2 #include <algorithm>
     3 
     4 const int maxn = 300;
     5 int gcd[maxn+10][maxn+10];
     6 
     7 int GCD(int a, int b)
     8 {
     9     return b == 0 ? a : GCD(b, a%b);
    10 }
    11 
    12 int main()
    13 {
    14     for(int i = 1; i <= maxn; ++i)
    15         for(int j = 1; j <= i; ++j)
    16             gcd[i][j] = gcd[j][i] = GCD(i, j);
    17 
    18     int n, m;
    19     while(scanf("%d%d", &n, &m) == 2 && n)
    20     {
    21         int ans = 0;
    22         for(int a = 1; a <= n; ++a)
    23             for(int b = 1; b <= m; ++b)
    24                 if(gcd[a][b] == 1)
    25                 {
    26                     int c = std::max(0, n-a*2) * std::max(0, m-b*2);
    27                     ans += (n-a)*(m-b) - c;
    28                 }
    29 
    30         printf("%d
    ", ans * 2);
    31     }
    32 
    33     return 0;
    34 }
    代码君

    解法二:

    解法转自UVA 1393 - Highways (容斥原理计数)

    dp(i, j)表示在长宽为ij的盒子中,从左上角最多能连多少条不重复的直线。

    可以根据容斥原理递推dp(i, j) = dp(i-1, j) + dp(i, j-1) - dp(i-1, j-1) + (gcd(i, j) = 1) (因为盒子两边长互质的时候,才能连出一条新边出来)

    最后答案ans递推的形式也是一样的,但重复的连线是那些缩小两倍后仍存在的直线

    ans(i, j) = ans(i-1, j) + ans(i, j-1) - ans(i-1, j-1) + dp(i, j) - dp(i/2, j/2)

    最后代码中,本想着只计算一半答案会快一点,结果排名21,登榜失败。

     1 #include <cstdio>
     2 #include <algorithm>
     3 
     4 const int maxn = 300;
     5 int dp[maxn+1][maxn+1], ans[maxn+1][maxn+1];
     6 
     7 int gcd(int a, int b)
     8 {
     9     return b == 0 ? a : gcd(b, a%b);
    10 }
    11 
    12 void Init()
    13 {
    14     for(int i = 1; i <= maxn; ++i)
    15         for(int j = 1; j <= i; ++j)
    16         {
    17             if(i == j) dp[i][j] = dp[i][j-1] * 2 - dp[i-1][j-1] + (gcd(i, j) == 1);
    18             else dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + (gcd(i, j) == 1);
    19         }
    20 
    21     for(int i = 1; i <= maxn; ++i)
    22         for(int j = 1; j <= i; ++j)
    23         {
    24             if(i == j) ans[i][j] = ans[i][j-1] * 2 - ans[i-1][j-1] + dp[i][j] - dp[i/2][j/2];
    25             else ans[i][j] = ans[i][j-1] + ans[i-1][j] - ans[i-1][j-1] + dp[i][j] - dp[i/2][j/2];
    26         }
    27 }
    28 
    29 int main()
    30 {
    31     Init();
    32     int n, m;
    33     while(scanf("%d%d", &n, &m) == 2 && n)
    34     {
    35         if(n < m) std::swap(n, m);
    36         printf("%d
    ", ans[n-1][m-1]*2);
    37     }
    38 
    39     return 0;
    40 }
    代码君
  • 相关阅读:
    11C++11通用为本,专用为末_2
    10C++11通用为本,专用为末_1
    09C++11保证稳定性和兼容性
    21变量名的力量_2
    NOIP2018 游记
    CF767C 经典的树形DP
    CF1A Theatre Square
    洛谷P1720 月落乌啼算钱
    洛谷P3388 缩点
    NOIP2017D2T1 奶酪 洛谷P3958
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4199686.html
Copyright © 2011-2022 走看看