1. 程式人生 > >2016 CCPC Hangzhou Onsite(部分題)

2016 CCPC Hangzhou Onsite(部分題)

D題 - Difference

題意:給你兩個數$x$, $K$個數和一個公式$$f(y, K) = \sum_{z\text{ in every digits of }y} z^K (f(233, 2) = 2^2 + 3^2 + 3^2 = 22)$$

要求你找出所有滿足$$x = f(y, K) - y$$這個式子的y的數量。

解決:折半列舉法,這個套路其實被用過很多遍,但以前沒有研究過它,所以遇到這種題很少會往這個方向去想導致這種水題都難出。系統學習其實是自學的一種很好的途徑,以後要形成這種學習意識才對。

廢話說了這麼多,我們來談談這個題。這個題知道方法後就很簡單了,我們預處理低位對答案的貢獻,然後列舉高位即可。所有位對答案的貢獻其實就是$$\sum_{i=1}^{length} (num[i]^k - num[i]*10^i)$$

既然每位相對獨立,折半列舉就不存在問題了。

程式碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 const int N = 9 + 5;
 7 const int M = 100000;
 8 
 9 map <ll, int> mp[N];
10 int arry[N];
11 ll pw[N][N];
12 ll p10[N];
13 
14 void init()
15 {
16     for
(int i = 0; i < N; ++ i) { 17 pw[i][0] = 1; 18 for(int j = 1; j < N; ++ j) 19 pw[i][j] = pw[i][j-1] * i; 20 } 21 p10[0] = 1; 22 for(int i = 1; i < N; ++ i) { 23 p10[i] = p10[i-1] * 10; 24 } 25 for(int i = 0; i < M; ++ i) { 26 int
t = i, cnt = 0; 27 while(t) { 28 arry[cnt ++] = t % 10; 29 t /= 10; 30 } 31 for(int k = 1; k <= 9; ++ k) { 32 ll ans = 0; 33 for(int j = 0; j < cnt; ++ j) 34 ans += pw[arry[j]][k]; 35 mp[k][ans - 1ll * i] ++; 36 } 37 } 38 } 39 40 int main() 41 { 42 int T, kase = 1; 43 init(); 44 scanf("%d", &T); 45 while(T --) { 46 ll x, k; 47 ll ans = 0; 48 scanf("%lld%lld", &x, &k); 49 for(int i = 0; i < M; ++ i) { 50 int tmp = i; 51 ll sec = x; 52 while(tmp) { 53 sec -= pw[tmp % 10][k]; 54 tmp /= 10; 55 } 56 sec += p10[5] * i; 57 if(mp[k].count(sec)) 58 ans += 1ll * mp[k][sec]; 59 } 60 if(x == 0) ans --; 61 printf("Case #%d: %lld\n", kase ++, ans); 62 } 63 return 0; 64 }
View Code

 

J題 - Just a Math Problem

題意:給你個$n$,要求你求出

$$\sum_{i = 1}^{n} g(i)$$

其中

$$g(i)=2^{f(i)}$$

$f(i)$表示的是$i$的素因子的個數。

解決:此題有兩種解法,都是值得大家去學習的。

解法一:利用莫比烏斯函式解決

莫比烏斯函式,他的符號為$\mu$,如果不知道這是什麼那麼可以把這種解法跳過,以後學習完這個概念後再來研究這種解法。

怎麼用這個函式解決這個題呢?

首先我們看一下莫比烏斯函式的一個推導公式

$$\mu^{2}(x) = \sum_{k^2|x}\mu(k)$$

- 公式1

要這個有什麼用呢?我們來分析題幹上的公式,$g(i)=2^{f(i)}$,這裡我們把它理解成$i$的無平方素因子的個數(仔細想想為什麼),而$\mu(i)$只有在$i$無平方素因子才會有值,值為$1$, $-1$。所以我們可以將其轉換成

$$g(i)=\sum_{d|i}\mu^{2}(d)$$

結合公式1我們得到

$$g(i)=\sum_{d|i}\sum_{k^2|d}\mu(k)$$

由整數的性質再結合題幹公式我們得到

$$\sum_{i=1}^{n}=\sum\limits_{k=1}^{n}\mu(k)\sum_{k^2|d}\lfloor \frac{n}{d} \rfloor$$

至此,我們可以列舉$k$,當$k^2$大於n貢獻就為0了我們就可以停止列舉,分塊求出$$\sum_{k^2|d}\lfloor \frac{n}{d} \rfloor$$的值累加進答案即可。時間複雜度為$O(\sqrt{n}log(n))$。

這種方法有三難,難想,難推,難過。為什麼會難過?嗯,這種方法被卡常卡的很厲害。你需要進行各種細節優化才能過。

程式碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 const int N = 1e6 + 5;
 7 const ll MOD = 1e9 + 7;
 8 
 9 int mu[N];
10 int prime[N];
11 ll g[N], mem[N];
12 bool vis[N];
13 int cn;
14 
15 void init()
16 {
17   memset(mem, -1, sizeof(mem));
18   cn = 0;
19   mu[1] = 1;
20   for(int i = 2; i < N; ++ i) {
21     if(!vis[i]) {
22       prime[cn ++] = i;
23       mu[i] = -1;
24     }
25     for(int j = 0; j < cn && i * prime[j] < N; ++ j) {
26       vis[i * prime[j]] = 1;
27       if(i % prime[j] != 0) {
28         mu[i * prime[j]] = - mu[i];
29       }
30       else break;
31     }
32   }
33 }
34 
35 void fadd(ll& a, ll b)
36 {
37   a += b;
38   if(a >= MOD) a -= MOD;
39 }
40 
41 ll F(ll lim)
42 {
43   if(lim < N && mem[lim] != -1) return mem[lim];
44   ll tmp = 0;
45   for(ll j = 1, st; j <= lim; j = st + 1) {
46     st = lim / (lim / j);
47     tmp += (st - j + 1) * (lim / j);
48   }
49   if(lim < N) mem[lim] = tmp % MOD;
50   return tmp % MOD;
51 }
52 
53 int main()
54 {
55   init();
56   int T, kase = 1;
57   scanf("%d", &T);
58   while(T --) {
59     ll n;
60     scanf("%lld", &n);
61     int lim = sqrt(n) + 1;
62     ll ans = 0;
63     for(int i = 1; i <= lim; ++ i) if(mu[i]){
64       ans += mu[i] * F(n/i/i);
65     }
66     printf("Case #%d: %lld\n", kase ++, (ans % MOD + MOD) % MOD);
67   }
68   return 0;
69 }
View Code

在HDU上測得的狀態:

解法二:利用互質關係解決

在逛部落格時發現了一個更好的思路,這種解法對$g(i) = 2^{f(i)}$有另一種解釋:乘積為$i$且互質的有序數對個數(再想想這是為什麼)。

那麼我們列舉$i$,再利用容斥計算下$[i+1, n/i]$中有多少個與其互質即可。計算出累加和$sum$還不夠,因為是有序對,所以正真的答案為$ans = sum * 2 + 1$。

時間複雜度$O(能過)$

程式碼:

 1 #include <bits/stdc++.h>
 2 #define lowbit(x) ((x)&(-x))
 3 using namespace std;
 4 
 5 typedef long long ll;
 6 
 7 const int N = 1e6 + 5;
 8 const ll MOD = 1e9 + 7;
 9 
10 int prime[N];
11 ll mul[N];
12 int L[N];
13 bool vis[N];
14 int cn = 0;
15 
16 vector <int> vec[N];
17 
18 void init()
19 {
20   for(int i = 2; i < N; ++ i) {
21     if(!vis[i]) prime[cn ++] = i;
22     for(int j = 0; j < cn && prime[j] * i < N; ++ j) {
23       vis[prime[j] * i] = 1;
24       if(i % prime[j] == 0) break;
25     }
26   }
27   for(int i = 1; i < N; ++ i)
28   {
29     int cnt = 0, x = i;
30     for(int j = 0; j < cn; ++ j) {
31       if(prime[j] * prime[j] > x) break;
32       if(x % prime[j] == 0) {
33         vec[i].push_back(prime[j]);
34       }
35       while(x % prime[j] == 0) {
36         x /= prime[j];
37       }
38     }
39     if(x != 1) vec[i].push_back(x);
40   }
41   L[1] = 0;
42   for(int i = 1; (1<<i) < N; ++ i) {
43     L[1<<i] = i;
44   }
45 }
46 
47 ll cal(int x, ll n)
48 {
49   ll rtn = n - x;
50   mul[0] = 1;
51   for(int i = 1; i < (1<<(int)vec[x].size()); ++ i) {
52     int d = lowbit(i);
53     mul[i] = mul[i-d] * -vec[x][L[d]];
54     rtn += n / mul[i] - x / mul[i];
55   }
56   return rtn;
57 }
58 
59 int main()
60 {
61   int T, kase = 1;
62   init();
63   scanf("%d", &T);
64   while(T --) {
65     ll n, ans = 0;
66     scanf("%lld", &n);
67     for(int i = 1; 1ll * i * i <= n; ++ i) {
68       ans += cal(i, n / i);
69     }
70     printf("Case #%d: %lld\n", kase ++, (ans * 2 + 1) % MOD);
71   }
72   return 0;
73 }
View Code

在HDU測得的狀態:

 

感謝觀看,如有問題,歡迎在評論中指出。