[BZOJ2005][NOI2010]能量采集 数学

本文介绍了一种计算平面直角坐标系中不可见点数的方法,通过数学推导和算法实现,解决了给定范围内不可见点的快速计算问题,并提供了一段C++代码作为实现示例。

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2005

发现与$(0,0)$连线斜率相同的点会被挡住。也就是对于$(a,b)$且$gcd(a,b)==1$,在这条连线上$(da,db)$都会被挡住。

换种表达方式就是对于任意一个点$(x,y)$,会有$gcd(x,y)-1$个点被挡住。于是被挡住的点的个数是$$\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)-1$$

即答案为$$Ans=\sum_{i=1}^n\sum_{j=1}^m2*gcd(i,j)-1$$

$$\frac{Ans-n*m}{2}=\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)$$

现在的问题就在于如何快速求右边的东西。我们来接着推。

首先有一个东西$n=\sum_{d|n}φ(d)$,于是有$\sum_{i=1}^n\sum_{j=1}^mgcd(i,j)=$

$$\sum_{i=1}^n\sum_{j=1}^m\sum_{d|gcd(i,j)}φ(d)=$$

$$\sum_{d=1}^{min(n,m)}φ(d)\sum_{i=1}^n[d|i]\sum_{j=1}^m[d|j]=$$

$$\sum_{d=1}^{min(n,m)}φ(d)[\frac{n}{d}][\frac{m}{d}]$$

然后线性筛求欧拉函数,分块求和就行了。

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 bool vis[100010];
 7 int p[100010],pcnt=0;
 8 int phi[100010];
 9 ll sum[100010];
10 void sieve(){
11     sum[1]=1;
12     for(int i=2;i<=100000;i++){
13         if(!vis[i]){
14             vis[i]=true;
15             p[++pcnt]=i;
16             phi[i]=i-1;
17         }
18         for(int j=1;p[j]*i<=100000&&j<=pcnt;j++){
19             vis[p[j]*i]=true;
20             if(i%p[j]==0){
21                 phi[i*p[j]]=phi[i]*p[j];
22                 break;
23             }
24             phi[i*p[j]]=phi[i]*(p[j]-1);
25         }
26         sum[i]=sum[i-1]+phi[i];
27     }
28 }
29 int main(){
30     sieve();
31     int n,m;
32     scanf("%d%d",&n,&m);
33     int t=min(n,m),la;
34     ll ans=0;
35     for(int i=1;i<=t;i=la+1){
36         la=min(n/(n/i),m/(m/i));
37         ans+=(ll)(sum[la]-sum[i-1])*(n/i)*(m/i);
38     }
39     ans=ans*2-(ll)n*m;
40     printf("%lld\n",ans);
41     return 0;
42 }

 

转载于:https://www.cnblogs.com/halfrot/p/7598336.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值