codeforces Round 647(div. 2)
A、Johnny and Ancient Computer
题意:
给出$x$和$y$,每次只能$x$乘$8$,乘$4$,乘$2$,除$2$,除$4$,除$8$,选一个操作,求$x$变成$y$的最小操作次数。
题解:
首先我们先假定$x<y$,然后把它们表示成$r_1\times 2^{e_1}$,$r_2\times 2^{e_2}$,必须有$r_1=r_2$,在这个前提下,$x$不断乘$8$到下一次乘$8$大于$y$了,然后如果$x$最终等于$y$了,乘的次数就是答案。否则$x$一定可以乘$4$或者$2$使得$x=y$,这样子就次数$+1$就是答案。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int get(ll a) 5 { 6 while (a % 2 == 0) 7 a /= 2; 8 return a; 9 } 10 void solve() 11 { 12 ll a, b; 13 scanf("%lld%lld", &a, &b); 14 if (a > b) 15 swap(a, b); 16 if (get(a) != get(b)) 17 { 18 printf("-1\n"); 19 return; 20 } 21 b /= a; 22 int ans = 0; 23 while (b && b % 8 == 0) 24 ++ans, b /= 8; 25 if (b > 1) 26 ++ans; 27 printf("%d\n", ans); 28 } 29 int main() 30 { 31 int T; 32 scanf("%d", &T); 33 while (T--) 34 solve(); 35 return 0; 36 }
(黑科技:$OEIS$有公式,可以直接查)
B、Johnny and His Hobbies
题意:
给出一个集合,求能不能找到一个正数,使得这个集合中的所有数异或后的值组成的新集合,原集合一样。$\sum size\leq 1024,s_i\leq 1024$。
题解:
这个没啥规律的$QAQ$,直接暴力找就行了,上界是$1023$。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 set<int> s; 5 void solve() 6 { 7 s.clear(); 8 int n, a; 9 scanf("%d", &n); 10 for (int i = 1; i <= n; ++i) 11 { 12 scanf("%d", &a); 13 s.insert(a); 14 } 15 set<int> t; 16 int maxn = *(--s.end()); 17 int ans = -1; 18 for (int i = 1; i < (1 << 10); ++i) 19 { 20 t.clear(); 21 for (auto &j : s) 22 t.insert(i ^ j); 23 if (s == t) 24 { 25 ans = i; 26 break; 27 } 28 } 29 printf("%d\n", ans); 30 } 31 int main() 32 { 33 int T; 34 scanf("%d", &T); 35 while (T--) 36 solve(); 37 return 0; 38 }
C、Johnny and Another Rating Drop
题意:
给出$x$,求出从$0$到$x$的相邻数字二进制串的汉明距离和。
题解:
写出$1$,$2$,$3$,$4$,......,$16$的结果,然后可以找到规律,如果二进制串中某一位是$1$,结果就一定会加上一个定值,并且这个定值有规律,打个表然后查表就行了。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef unsigned long long ll; 4 ll num[80]; 5 void init() 6 { 7 num[0] = 1; 8 for (int i = 1; i <= 64; ++i) 9 num[i] = num[i - 1] + (1ull << i); 10 } 11 void solve() 12 { 13 ll n; 14 scanf("%llu", &n); 15 ll ans = 0; 16 for (int i = 0; n; n >>= 1, ++i) 17 if (n & 1) 18 ans += num[i]; 19 printf("%llu\n", ans); 20 } 21 int main() 22 { 23 init(); 24 int T; 25 scanf("%d", &T); 26 while (T--) 27 solve(); 28 return 0; 29 }
D、Johnny and Contribution
题意:
给出$n$的点的图,每个点有一种颜色,初始都没有涂色,求出一个涂色顺序,使得当前涂色的这个点的颜色必须是已经涂色的点的集合的$mex$,如果没有输出$-1$。
题解:
而且我们限定涂色顺序是从小的颜色到大的颜色。如果没有$1$直接$-1$。然后给它的邻接点更新连续数的$max$,这样子到了涂这个点的时候,它的颜色必须是$max+1$,然后再更新邻接点,否则$-1$。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 5e5 + 5; 4 vector<int> G[N]; 5 struct node 6 { 7 int v, id; 8 bool operator<(const node &a) const 9 { 10 return id == a.id ? v < a.v : id < a.id; 11 } 12 }; 13 int maxn[N]; 14 node e[N]; 15 vector<int> ans; 16 int main() 17 { 18 int n, m; 19 scanf("%d%d", &n, &m); 20 for (int i = 1; i <= m; ++i) 21 { 22 int u, v; 23 scanf("%d%d", &u, &v); 24 G[u].push_back(v); 25 G[v].push_back(u); 26 } 27 for (int i = 1; i <= n; ++i) 28 { 29 scanf("%d", &e[i].id); 30 e[i].v = i; 31 } 32 for (int i = 1; i <= n; ++i) 33 for (auto j : G[i]) 34 if (e[i].id == e[j].id) 35 return printf("-1\n"), 0; 36 sort(e + 1, e + n + 1); 37 for (int i = 1; i <= n; ++i) 38 { 39 int v = e[i].v; 40 if (e[i].id == maxn[v] + 1) 41 { 42 maxn[v] = e[i].id; 43 ans.push_back(v); 44 for (auto u : G[v]) 45 if (maxn[v] == maxn[u] + 1) 46 maxn[u] = maxn[v]; 47 } 48 else 49 return printf("-1\n"), 0; 50 } 51 for (int i = 0; i < ans.size(); ++i) 52 printf("%d%c", ans[i], " \n"[i == ans.size() - 1]); 53 return 0; 54 }
E、Johnny and Grandmaster
题意:
给出$n$个数,$p$,把这个$n$个数分成两个多重集合$s_1,s_2$。求$abs(\sum \limits _{i \in s_1} p^{i}-\sum \limits _{i \in s_2} p^{i})$最小。
题解:
解法一:
考虑把这$n$个数降序排序,初始的答案是$0$,然后考虑如果当前答案是$0$就加上当前数,否则减去当前数,因为要$mod \ (1e9+7)$,所以实际上变成当前初始答案$0$之后,差不一定是$0$,可能是模数的倍数,这样子,如果我们使用多模数哈希,只有这些模数都是$0$的时候,我们认为这个和确实到$0$了,然后最后输出模数是$1e9+7$的答案即可。为了尽量减小错误概率,模数必须是素数且尽量接近$1e9+7$,实际上只用$1e9+7$和$1e9+3$即可通过本题。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e6 + 5; 4 const int mod = 1e9 + 7; 5 const int mod1 = 1e9 + 3; 6 typedef long long ll; 7 int a[N]; 8 ll qpow(ll a, ll b, ll p) 9 { 10 ll res = 1; 11 while (b) 12 { 13 if (b & 1) 14 res = res * a % p; 15 a = a * a % p; 16 b >>= 1; 17 } 18 return res; 19 } 20 void solve() 21 { 22 int n, p; 23 scanf("%d%d", &n, &p); 24 for (int i = 1; i <= n; ++i) 25 scanf("%d", &a[i]); 26 sort(a + 1, a + n + 1, greater<int>()); 27 ll ans1 = 0, ans2 = 0; 28 for (int i = 1; i <= n; ++i) 29 { 30 if (!ans1 && !ans2) 31 { 32 ans1 = (ans1 + qpow(p, a[i], mod)) % mod; 33 ans2 = (ans2 + qpow(p, a[i], mod1)) % mod1; 34 } 35 else 36 { 37 ans1 = (ans1 - qpow(p, a[i], mod) + mod) % mod; 38 ans2 = (ans2 - qpow(p, a[i], mod1) + mod1) % mod1; 39 } 40 } 41 printf("%lld\n", (ans1 + mod) % mod); 42 return; 43 } 44 int main() 45 { 46 int T; 47 scanf("%d", &T); 48 while (T--) 49 solve(); 50 return 0; 51 }
解法二:
继续先降序排序,然后也还是加减的操作,只不过这里我们发现对于$p^k$,减的时候,一定会经过$0$,而不是直接从正减到负,同时,这个差一定能表示成$a \times p^b$。所以我们只需要保存$a$和$b$即可。然后考虑到这$n$个数的上界是$1e6$,如果$a$已经大于$1e6$了或者小于$-1e6$了,显然我们已经没有办法,让这个$a$回到$0$,这个时候直接让$a$是$1e6$或者$-1e6$即可,因为这个情况下,显然后面所有的数都应该减。实际上这个$1e6$是一个设置的上界而已,你也可以设置成$1e8$。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e6 + 5; 4 const int mod = 1e9 + 7; 5 typedef long long ll; 6 const ll INF = 1e6; 7 ll qpow(ll a, ll b, ll p) 8 { 9 ll res = 1; 10 while (b) 11 { 12 if (b & 1) 13 res = res * a % p; 14 a = a * a % p; 15 b >>= 1; 16 } 17 return res; 18 } 19 int a[N]; 20 int nowk, n; 21 ll dif, p, ans; 22 inline void cal(int x) 23 { 24 ans = ans * qpow(p, nowk - x, mod) % mod; 25 if (dif) 26 for (int i = 1; i <= nowk - x; ++i) 27 { 28 dif = max(min(dif * p, INF), -INF); 29 if (dif == 1e6 || dif == -1e6) 30 break; 31 } 32 nowk = x; 33 } 34 inline void push(int x) 35 { 36 cal(x); 37 if (dif > 0) 38 --ans, --dif; 39 else 40 ++ans, ++dif; 41 } 42 void solve() 43 { 44 scanf("%d%lld", &n, &p); 45 for (int i = 1; i <= n; ++i) 46 scanf("%d", &a[i]); 47 sort(a + 1, a + n + 1, greater<int>()); 48 if (p == 1) 49 { 50 printf("%d\n", n % 2); 51 return; 52 } 53 ans = dif = 0; 54 nowk = N; 55 for (int i = 1; i <= n; ++i) 56 push(a[i]); 57 cal(0); 58 printf("%lld\n", (ans + mod) % mod); 59 } 60 int main() 61 { 62 int T; 63 scanf("%d", &T); 64 while (T--) 65 solve(); 66 return 0; 67 }