Multiple query, for each n, you need to get
n i-1
∑ ∑ [gcd(i + j, i - j) = 1]
i=1 j=1
Input
On the first line, there is a positive integer T, which describe the number of queries. Next there are T lines, each line give a positive integer n, as mentioned above.
T<=1e5, n<=2e7
Output
Your output should include T lines, for each line, output the answer for the corre- sponding n.
Sample Input
4 978 438 233 666
Sample Output
194041 38951 11065 89963
题目问, 也就是 对于不同的i,我们要统计 符合条件的数量,一开始我想的是莫比乌斯反演,没反出来,因为这个 i,j,关联在一起了。
我们考虑一下 GCD, 如果 (x,y)=1,那么 (x,y-x)=1, 辗转想减法证明一下就好。
可以发现, 其实也就是总和为
的两个数互质的数量,通过上面我们可以知道,如果
的话,那么
,所以问题就转化成了小于
,并且与
互质的数量。但是一个数字要大于
,一个数字要小于
。可以这么考虑,不考虑大于i,或者小于i,直接考虑全部,那么就是
,对于每一个互质的数字a,都会有对应的一个b=(2*i-a)也会与
互质,所以相当于我们计算了两倍, 那么对于每一个
,答案就是
, 然后把对应的i,全都加起来就好了
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<b;i++)
typedef long long LL;
//直接读取文件,高级版,但是不能直接输入,只能文件来搞
namespace fastIO {
#define BUF_SIZE 100000
//fread -> read
bool IOerror = 0;
inline char nc() {
//FILE* fp=fopen("123.txt","r");
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if(p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);//从显示器读入的话,改为stdin,文件的话,改为相应的文件指针fp
if(pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) {
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
inline void read(int &x) {
char ch;
while(blank(ch = nc()));
if(IOerror) return;
for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
inline void print(long long x){//long long 类型的输出
static int cnt;
static int a[20];
cnt=0;
do{
a[++cnt]=x%10;
x/=10;
}while(x);
for(int i=cnt;i>=1;i--)putchar(a[i]+'0');
puts("");
}
#undef BUF_SIZE
};
using namespace fastIO;
//贾志鹏线性筛 欧拉函数
//begin
const int N=4e7+10;//!!!记住修改N
int phi[N],prime[N],tot=0;
bool vis[N];
void getphi(){
phi[1]=1;
for(int i=2;i<N;i++)//相当于分解质因式的逆过程
{
if(!vis[i]){
prime[tot++]=i;//筛素数的时候首先会判断i是否是素数。
phi[i]=i-1;//当 i 是素数时 phi[i]=i-1
}
for(int j=0;j<tot;j++){
if(i*prime[j]>=N) break;
vis[i*prime[j]]=1;//确定i*prime[j]不是素数
if(i%prime[j]==0)//接着我们会看prime[j]是否是i的约数
{
phi[i*prime[j]]=phi[i]*prime[j];break;
}
else phi[i*prime[j]]=phi[i]*(prime[j]-1);//其实这里prime[j]-1就是phi[prime[j]],利用了欧拉函数的积性
}
}
}
//end
LL sum[N];
void getans(){
rep(i,1,N/2){
sum[i]=sum[i-1]+phi[i<<1]/2 ;
}
}
int main(){
getphi();
getans();
int T;
read(T);
//scanf("%d",&T);
while(T--){
int n;
read(n);
//scanf("%d",&n);
print(sum[n]);
}
return 0;
}
当然这么计算效率有点低,考虑对于每个,
, 所以结果可以写为
,这样的话,就只需要计算前n项了