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测得的状态:

 

感谢观看,如有问题,欢迎在评论中指出。

posted @ 2018-12-05 11:57  UtopioSPH  阅读(221)  评论(0编辑  收藏  举报