TIOJ-1241/1325

從前,有個古老流傳的單人遊戲是這樣的
從1~n的整數中,你可以選擇把每個數當成兩種類別的其中一種:”倍數” 和 “因數”
被歸類為”因數”的數字沒有得分,但當然的,它是有用處的,看了下一句就知道;
被歸類為”倍數”的數字(假設是M)之得分,是所有被歸類為”因數”且整除M的數字的個數。
所以舉例來說, 假設1,2,5被歸為”因數” ,3,4,6,7,8,9,10被歸為”倍數”, 總得分就是1+2+2+1+2+1+3=12。

據說有人發明了”最強之無敗策略”,可以達到最佳的總分
這種神奇的策略, 就是”倍因的道”,或簡稱”倍因道”。

總之,希望你也懂得這個道
因為給定n,要請你回答最大可能得到的得分!

TIOJ-1241: $n\le 2000$

TIOJ-1325: $t, n\le 10^5$


1241

一個困難版一個簡單版,先從1241開始。
對於每個數字,去計算它的因數中被歸類為”因數”的數量及倍數數量,如果倍數數量>前者,那這樣直接把這個數字變成”因數”答案肯定會變好。
所以就有個$\mathcal{O}(NlogN)$的做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
#define mem(i,j) memset(i,j,sizeof (i));
using ll = int64_t;
using ull = uint64_t;
using ld = long double;
using uint = uint32_t;
const double EPS = 1e-8;
const int INF = 0x3F3F3F3F;
const ll LINF = 4611686018427387903;
const int MOD = 1e9+7;
/*-----------------------------------------------------------------------------------------------------*/

signed main() {
EmiliaMyWife

int t;
cin >> t;
while(t--) {
int n;
cin >> n;
vector<int> ans(n + 1);
for(int i = 1; i <= n; i++) {
int res = 0;
for(int j = i * 2; j <= n; j+=i)
res++;
if(res >= ans[i]) {
ans[i] = 0;
for(int j = i * 2; j <= n; j+=i)
ans[j]++;
}
}
cout << accumulate(ans.begin(), ans.end(), 0) << '\n';
}

return 0;
}

1325

這題中複雜度$\mathcal{O}(TNlogN)$會太高,得往建表的方面想。

去想想n變成n+1時會發生什麼情況?
他的所有因數的倍數數量都會多一
此時就能檢查1241的條件:倍數數量>前者。
而把它變成”因數”會需要$\mathcal{O}(logN)$,最多只會有$N$個數字被變成因數。

所以複雜度會變成$\mathcal{O}(NloN+N\sqrt N)$(用平方根其實沒那麼準確,因數數量有個更緊的上界(但我不知道是多少))。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
#define mem(i,j) memset(i,j,sizeof (i));
using ll = int64_t;
using ull = uint64_t;
using ld = long double;
using uint = uint32_t;
const double EPS = 1e-8;
const int INF = 0x3F3F3F3F;
const ll LINF = 4611686018427387903;
const int MOD = 1e9+7;
/*-----------------------------------------------------------------------------------------------------*/

const int N = 1e5 + 1;
vector<int> fac[N];
int cnt[N], ans[N], res, out[N];

signed main() {
EmiliaMyWife

for(int i = 1; i < N; i++) {
for(int j = i * 2; j < N; j+=i)
fac[j].push_back(i);
}
for(int i = 1; i < N; i++) {
res += ans[i];
for(int a : fac[i]) {
if(cnt[a] < 0)
continue;
cnt[a]++;
if(cnt[a] >= ans[a]) {
res -= ans[a];
res += cnt[a];
ans[a] = 0;
cnt[a] = -1;
for(int j = a * 2; j < N; j += a) {
ans[j]++;
}
}
}
out[i] = res;
}
int t, x;
cin >> t;
while(t--)
cin >> x, cout << out[x] << '\n';

return 0;
}