[NOI2010] 能量采集

能量采集

哎,数学真的好难 委屈巴巴.jpg


题意概述

在一个 \(n * m\) 的矩形中,每个点与原点(0,0)所在的线段与矩形内的点(不包括其本身)相交的数量为 \(k\)。求 \(\sum\limits_{i=1}^n\) \(\sum\limits_{j=1}^m\) \(2k_{ij} + 1\)

思路分析

打开一个作图软件,随便画几个点你就会发现题目要求的是啥。由于这个屑太懒,就不画了

咦?!那就是每个点 \((x,y)\)\(\gcd(x,y)\) 吗?!

所以如果你做了P1390,那么事情就会变得很简单~

但是要注意,题目要求的 \(k\) 是除去该点本身的,但 \(\gcd(x,y)\) 是包含了它自己,所以推出的公式就要转换一下:

\[Ans =\sum_{i=1}^n\sum_{j=1}^m2 * gcd(i,j) - 1 \]

\[= 2 * \sum_{i=1}^n\sum_{j=1}^mgcd(i,j) - n * m \]

\[= 2 * \sum_{i=1}^n\sum_{j=1}^mgcd(i,j) - n * m \]

哎呀呀,好难打呀。又懒又笨的博主肝不动了,想看具体公式的,去洛谷题解区~

.....此处省略一万字.....

最后得出:

\[Ans = 2 * \sum_{d=1}^{min(n,m)} \lfloor \frac n d \rfloor * \lfloor \frac m d \rfloor \varphi(d) \]

敲公式真的好难啊╥﹏╥...

看到赤裸裸的 $ \lfloor \frac n d \rfloor * \lfloor \frac m d \rfloor$ 了吗?

整除分块呀。

然后就没了。。。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define rt register int
#define int long long
const int N = 1e5;
int cnt,prim[N + 5];
long long mu[N + 5],ans;
bool vis[N + 5];
inline void init(int nn) {
	mu[1] = vis[1] = 1;
	rt i,j; 
	for(i = 2; i <= nn; i ++) {
		if(!vis[i]) {
			mu[i] = i - 1;
			prim[++cnt] = i;
		} 
		for(j = 1; j <= cnt && i * prim[j] <= nn; j ++) {
			vis[i * prim[j]] = 1;
			if(i % prim[j] == 0) {
				mu[i * prim[j]] = mu[i] * prim[j];
				break;
			}
			else mu[i * prim[j]] = mu[i] * (prim[j] - 1);
		}
	}
	for(i = 1; i <= nn; i ++) mu[i] += mu[i - 1];
} 
inline int Min(int x,int y) {
	return x < y ? x : y;
}
inline long long cale(int n,int m) {
	long long res = 0;
	for(rt l = 1,r; l <= Min(n,m); l = r + 1) {
		r = Min(n / (n / l),m / (m / l));
		res += 1LL * (n / l) * (m / l) * (mu[r] - mu[l - 1]);
	}
	return res;
}
char s;
inline int read()
{
   int x=0,f=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){
       if(ch=='-')
           f=-1;
       ch=getchar();
   }
   while(ch>='0'&&ch<='9'){
       x=(x<<1)+(x<<3)+(ch^48);
       ch=getchar();
   }
   return x*f;
}
inline void write(long long x) {
	if(x < 0) {x = -x; putchar('-');};
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
signed main() {
	int n,m;
	n = read(), m = read();
	init(min(n,m));
	ans = 2 * cale(n,m) - n * m;
	write(ans);
	return 0;
} 
posted @ 2021-02-03 22:15  Spring-Araki  阅读(71)  评论(0编辑  收藏  举报