首页 > 代码库 > BZOJ 2005 [Noi2010]能量采集

BZOJ 2005 [Noi2010]能量采集

2005: [Noi2010]能量采集

Description

栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量。在这些植物采集能量后,栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起。 栋栋的植物种得非常整齐,一共有n列,每列有m棵,植物的横竖间距都一样,因此对于每一棵植物,栋栋可以用一个坐标(x, y)来表示,其中x的范围是1至n,表示是在第x列,y的范围是1至m,表示是在第x列的第y棵。 由于能量汇集机器较大,不便移动,栋栋将它放在了一个角上,坐标正好是(0, 0)。 能量汇集机器在汇集的过程中有一定的能量损失。如果一棵植物与能量汇集机器连接而成的线段上有k棵植物,则能量的损失为2k + 1。例如,当能量汇集机器收集坐标为(2, 4)的植物时,由于连接线段上存在一棵植物(1, 2),会产生3的能量损失。注意,如果一棵植物与能量汇集机器连接的线段上没有植物,则能量损失为1。现在要计算总的能量损失。 下面给出了一个能量采集的例子,其中n = 5,m = 4,一共有20棵植物,在每棵植物上标明了能量汇集机器收集它的能量时产生的能量损失。 在这个例子中,总共产生了36的能量损失。

Input

仅包含一行,为两个整数n和m。

Output

仅包含一个整数,表示总共产生的能量损失。

Sample Input

【样例输入1】
5 4
【样例输入2】
3 4

Sample Output

【样例输出1】
36
【样例输出2】
20
对于100%的数据:1 ≤ n, m ≤ 100,000

  我们本来只打算讲一个晚上的数论,但是时间并不够。后来,又讲了一个上午,然而并没有讲完。于是下午再讲,基本上已无人再听。
  身心俱疲。课件在这里
  这道题要求一个n*m的矩阵中各整点与(0,0)连线上的整点总数。
由BZOJ 3505 [Cqoi2014]数三角形中推得,(x,y)与(0,0)上有k=gcd(x,y)-1个整点。而此处要求∑∑k*2+1=∑∑gcd(i,j)*2-1
  这道题:∑∑gcd(i,j)=∑∑∑[d|i][d|j]phi(d)=∑(n/d)(m/d)phi(d)
  因为:n=∑phi(d)。
  明显先用欧拉筛,算出phi的前缀和。再分块,一个块中(n/i)(m/i)都相等。所以是O(n)预处理,O(sqrt(n))的单次询问(当然O(n)亦可)。
  但是我们也可以使用容斥原理,算出∑∑gcd(i,j)。复杂度是O(nlg n)的。
  代码如下(调了很久……前缀和导致的变化,long long的取值):
技术分享
 1 /************************************************************** 2     Problem: 2005 3     User: Doggu 4     Language: C++ 5     Result: Accepted 6     Time:4 ms 7     Memory:2092 kb 8 ****************************************************************/ 9  10 #include <cstdio>11 #include <algorithm>12 const int N = 100100;13 int n, m, prime[N], ptot;14 long long phi[N], ans;15 bool vis[N];16 void EULER(int n) {17     phi[1]=1;18     for( int i = 2; i <= n; i++ ) {19         if(!vis[i]) prime[++ptot]=i, phi[i]=i-1;20         for( int j = 1; j <= ptot; j++ ) {21             if((long long)i*prime[j]>n) break;22             vis[i*prime[j]]=1;23             phi[i*prime[j]]=phi[i]*(prime[j]-1);24             if(i%prime[j]==0) {25                 phi[i*prime[j]]=phi[i]*prime[j];26                 break;27             }28         }29         phi[i]+=phi[i-1];30     }31 }32 int main() {33     scanf("%d%d",&n,&m);34     if(n>m) std::swap(n,m);35     EULER(n);36     for( int a = 1, ed; a <= n; a=ed+1 ) {37         ed=std::min(n/(n/a),m/(m/a));38         ans+=(long long)(phi[ed]-phi[a-1])*(n/a)*(m/a);39     }40     printf("%lld\n",ans*2-(long long)n*m);41     return 0;42 }
狄利克雷卷积+欧拉筛+分块

BZOJ 2005 [Noi2010]能量采集