[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;
}