题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=121873#problem/F
题目大意:给你一个数n,让你输出(i=1->n-1)(j=i+1->n)gcd(i,j)
思路分析:直接暴力做铁定超时,而且数据也不止一组,因此我们要考虑打表来做这一道题, 既然要打表,就要
寻找递推关系,手写一下,比较容易就可以找到递推关系,S[n]=S[n-1]+f[n]
f[n]=gcd(1,n)+gcd(2,n)+.......+gcd(n-1,n),现在问题就转化成了如何求f[n],直接求目测超时,这一步就
比较奇妙,我们可以从约数开始入手,看约数为1.2.....分别有多少个数,数的个数刚好为phi[j/i]
代码:
#include <iostream> #include <cmath> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int maxn=4000000+100; ll phi[maxn]; ll prime[maxn/10]; ll s[maxn]; ll f[maxn]; bool check[maxn]; int tot; void make_phi() { phi[1]=1; memset(check,true,sizeof(check)); tot=0; for(ll i=2;i<maxn;i++) { if(check[i]) { prime[tot++]=i; phi[i]=i-1; } for(int j=0;j<tot&&i*prime[j]<=maxn;j++) { check[i*prime[j]]=false; if(i%prime[j]==0) { phi[i*prime[j]]=phi[i]*prime[j]; } else phi[i*prime[j]]=phi[i]*(prime[j]-1); } } } void init() { memset(f,0,sizeof(f)); for(int i=1;i<maxn;i++)//打表求f[i] { for(int j=2*i;j<=maxn;j+=i) { f[j]+=i*phi[j/i]; } } s[1]=0; for(ll i=2;i<maxn;i++) { s[i]=s[i-1]+f[i]; } } int main() { ll n; make_phi(); init(); while(scanf("%lld",&n)&&n) { printf("%lld ",s[n]); } }