51Nod 1180 - 方格射击游戏(莫比乌斯反演)
题目链接 https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1180
【题目描述】
M*N的方格矩阵,一个人在左下角格子的中心,除他所站位置外,其他格子的中心都有一个敌人,他一次可发射一枚子弹干掉一条直线上的所有敌人,问至少要发射多少子弹才能干掉所有敌人
Input
输入2个数m, n,中间用空格分隔,对应矩阵的大小。(1 <= m,n <= 5 * 10^6)
Output
输出发射子弹的数量。
Input示例
2 10
Output示例
11
【思路】
把他自己和敌人抽象成坐标,自己的位置在点,坐标轴上必须开两枪,然后就是第一象限的点,对于某个点,当且仅当时才有必要开一枪,所以问题等价于中有多少对满足,莫比乌斯反演后计算即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5000005;
bool vis[maxn];
int prim[maxn];
int mu[maxn],sum[maxn];
int cnt;
void get_mu(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prim[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt && prim[j]*i<=n;j++){
vis[prim[j]*i]=1;
if(i%prim[j]==0) break;
else mu[i*prim[j]]=-mu[i];
}
}
for(int i=1;i<=n;++i) sum[i]=sum[i-1]+mu[i];
}
int main(){
get_mu(maxn-1);
int n,m;
scanf("%d%d",&n,&m);
if(n==1 && m==1) puts("0");
else if(n==1 || m==1) puts("1");
else{
--n;--m;
if(n>m) swap(n,m);
ll ans=2;
for(int L=1,R;L<=n;L=R+1){
R=min(n/(n/L),m/(m/L));
ans+=(ll)(sum[R]-sum[L-1])*(ll)(n/L)*(ll)(m/L);
}
printf("%lld\n",ans);
}
return 0;
}