Sum

【问题描述】

给定一个正整数 N ( N <= 231 - 1 )

求:

Sum BZOJ 3944

【输入格式】

一共T+1行
第1行为数据组数T(T<=10)
第2~T+1行每行一个非负整数N,代表一组询问

【输出格式】

一共T行,每行两个用空格分隔的数ans1,ans2
【样例输入】

6
1
2
8
13
30
2333

【样例输出】

1 1
2 0
22 -2
58 -3
278 -3
1655470 2


题解

首先推一波式子

Sum BZOJ 3944

上式就是杜教筛的原理

Sum BZOJ 3944

 

 

Sum BZOJ 3944:

Sum BZOJ 3944

Sum BZOJ 3944

 

 

 Sum BZOJ 3944:

 

Sum BZOJ 3944

 Sum BZOJ 3944

 

对于Sum BZOJ 3944的求解方式在上面已经给出了

那么求出前Sum BZOJ 3944项答案并记忆化状态,就能达到Sum BZOJ 3944的时间复杂度

由于 Sum BZOJ 3944 ,那么我们求的每一项的参数都是 Sum BZOJ 3944 形式的

对于 Sum BZOJ 3944 小于等于Sum BZOJ 3944的答案我们已经预处理出了

所以我们只需要记忆大于Sum BZOJ 3944的答案

首先提出一个命题:对于 Sum BZOJ 3944Sum BZOJ 3944 都不相同

证明:

假设 Sum BZOJ 3944,且Sum BZOJ 3944

那么Sum BZOJ 3944

mi , m表示两者的余数,它们的差为Sum BZOJ 3944

因为Sum BZOJ 3944,所以Sum BZOJ 3944

那么Sum BZOJ 3944,而Sum BZOJ 3944,假设不成立

所以不存在Sum BZOJ 3944,使得Sum BZOJ 3944

证毕

所以在 Sum BZOJ 3944时,Sum BZOJ 3944成立

那么Sum BZOJ 3944,我们就能直接使用数组存,对于每一个参数,我们将其除k的结果作为下标储存答案

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 using namespace std;
 8 const int maxm = 2e6 + 1;
 9 const int maxn = 1e4 + 1;
10 int n;
11 int pri[maxm];
12 bool vis[maxm];
13 struct couple
14 {
15     long long miu, phi;
16 };
17 couple ans[maxn], ori[maxm];
18 inline void Scan(int &x)
19 {
20     char c;
21     bool o = false;
22     while(!isdigit(c = getchar())) o = (c != '-') ? o : true;
23     x = c - '0';
24     while(isdigit(c = getchar())) x = x * 10 + c - '0';
25     if(o) x = -x;
26 }
27 int m;
28 inline void Sieve()
29 {
30     int tot = 0;
31     m = maxm - 1;
32     ori[1] = (couple) {1, 1};
33     for(int i = 2; i <= m; ++i)
34     {
35         if(!vis[i])
36         {
37             pri[++tot] = i;
38             ori[i] = (couple) {-1, i - 1};
39         }
40         for(int j = 1; j <= tot; ++j)
41         {
42             int k = pri[j];
43             long long s = (long long) i * k;
44             if(s > m) break;
45             vis[s] = true;
46             if(!(i % k))
47             {
48                 ori[s].miu = 0;
49                 ori[s].phi = ori[i].phi * k;
50                 break;
51             }
52             else
53             {
54                 ori[s].miu = -ori[i].miu;
55                 ori[s].phi = ori[i].phi * ori[k].phi;
56             }
57         }
58     }
59     for(int i = 1; i <= m; ++i)
60     {
61         ori[i].miu += ori[i - 1].miu;
62         ori[i].phi += ori[i - 1].phi;
63     }
64 }
65 couple Solve(int x)
66 {
67     if(x <= m) return ori[x];
68     int e = n / x;
69     if(vis[e]) return ans[e];
70     int last;
71     couple c, s;
72     s.miu = 1, s.phi = x * ((long long) x + 1) >> 1;
73     for(long long i = 2; i <= x; i = (long long) last + 1)
74     {
75         last = x / (x / i);
76         c = Solve(x / i);
77         s.miu -= c.miu * (long long) (last - i + 1), s.phi -= c.phi * (long long) (last - i + 1);
78     }
79     vis[e] = true, ans[e] = s;
80     return s;
81 }
82 int main()
83 {
84     int T;
85     Scan(T);
86     Sieve();
87     while(T--)
88     {
89         Scan(n);
90         memset(vis, false, sizeof(vis));
91         if(n <= m) printf("%lld %lld\n", ori[n].phi, ori[n].miu);
92         else
93         {
94             couple answer = Solve(n);
95             printf("%lld %lld\n", answer.phi, answer.miu);
96         }
97     }
98

相关文章: