• 51nod 1180 方格射击游戏


    M*N的方格矩阵,一个人在左下角格子的中心,除他所站位置外,其他格子的中心都有一个敌人,他一次可发射一枚子弹干掉一条直线上的所有敌人,问至少要发射多少子弹才能干掉所有敌人。

    Input
    输入2个数m, n,中间用空格分隔,对应矩阵的大小。(1 <= m,n <= 5 * 10^6)
    Output
    输出发射子弹的数量。


    首先,我们知道有一个结论,从坐标(0,0)到整点(x,y)连一条线段,经过的整点个数有gcd(x,y)+1
    个,包括(0,0)和(x,y)这2个。
    对于每一个坐标(x,y),我们需要考虑是不是需要专门去向它打一枪
    需要专门向(x,y)打一枪,说明(0,0)到(x,y)之间没有其他点挡着,即这条线段的整点数只有2个,
    即gcd(x,y) + 1 = 2,即gcd(x,y) = 1
    所以,如果坐标(x,y)满足gcd(x,y) = 1,则对答案贡献为1
    则得到公式
    ans = Σ0<=i<=n-1Σ0<=j<=m-1[gcd(i,j) = 1]
        = 2 + ∑1<=i<=n-11<=j<=m-1[gcd(i,j) = 1]
    令n=n-1,m=m-1,且n<=m,则:
    ans = 2 + ∑1<=i<=n1<=j<=m[gcd(i,j) = 1]
    令f(d)表示1<=i<=n,1<=j<=m,中gcd(i,j) = d的(i,j)对数
      g(d)表示1<=i<=n,1<=j<=m,中gcd(i,j)为d的倍数的(i,j)对数
    则有:
    g(d) = (n / d) * (m / d)
    g(d) = f(d) + f(2*d) + ... + f(n/d * d)
         = ∑d|kf(k)
    则有:
    f(d) = ∑d|kmu(k/d) * g(k)
    f(1) = ∑1<=k<=nmu(k) * g(k)
    ans = 2 + f(1)
    所以只需要求f(1)就可以了,可以O(n)求,也可以O(sqrt(n))
    特判:
    n == 1 && m == 1 ans = 0
                                                
      //File Name: nod1180.cpp
      //Created Time: 2017年01月02日 星期一 18时23分52秒
                                       
    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int MAXN = 5000000 + 2;
    int prime[MAXN / 10],mu[MAXN];
    bool check[MAXN];
    void init(int N){
        memset(check,false,sizeof(check));
        int tot = 0;
        mu[1] = 1;
        for(int i=2;i<=N;++i){
            if(!check[i]){
                prime[tot++] = i;
                mu[i] = -1;
            }
            for(int j=0;j<tot;++j){
                if((LL)i * prime[j] > N) break;
                check[i * prime[j]] = true;
                if(i % prime[j] == 0){
                    mu[i * prime[j]] = 0;
                    break;
                }
                else
                    mu[i * prime[j]] = -mu[i];
            }
        }
        for(int i=1;i<=N;++i)
            mu[i] += mu[i - 1];
    }
    LL solve(int n,int m){
        if(n == 1 && m == 1) return 0;
        if(n == 1 || m == 1) return 1;
        --n,--m;
        if(n > m) swap(n,m);
        init(n);
        LL res = 2;
        for(int i=1,x,y,r;i<=n;){
            x = n / i;
            y = m / i;
            r = min(n / x,m / y);
            res += 1LL * (mu[r] - mu[i-1]) * x * y;
            i = r + 1;
        }
        return res;
    }
    int main(){
        int n,m;
        scanf("%d %d",&n,&m);
        printf("%lld
    ",solve(n,m));
        return 0;
    }







     






  • 相关阅读:
    企业网站常用中英文对照表
    AttachJSFunction(一个button同时挂两个onclick事件)
    Js 整理
    宝玉的CSS
    网页中一些比较隐蔽的用法 作者:wbc
    flex中flexgrow作用
    flex实现换行内容上下贴边效果
    flex中自动换行设置,以及上下间距的设置?
    flex中aligncontent和aliginitems区别?
    正则表达式语法及实例整理[转]
  • 原文地址:https://www.cnblogs.com/-maybe/p/6242965.html
Copyright © 2020-2023  润新知