A. Find Minimum Operations
输入n,k,不断用\(n-k^x\) 代替\(n\) ,直到n等于0,问最少要多少步
快速幂计算,注意如果k=1,直接加上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 #include <iostream> #define int long long using namespace std;const int N = 1e4 + 10 ;int a[N];int qmi (int c, int b) { int ans = 1 ; while (b) { if (b & 1 ) ans = ans * c; b >>= 1 ; c = c * c; } return ans; } void solve () { int n, k; cin >> n >> k; if (k == 1 ) { cout << n << '\n' ; return ; } int i = 0 ; for (;i == 0 || a[i - 1 ] <= n; i++) a[i] = qmi (k, i); int step = 0 ; for (int j = i - 1 ; n > 0 ; j--) { if (a[j] == 1 ) { step += n; break ; } while (a[j] <= n) { n -= a[j]; step++; } } cout << step << '\n' ; return ; } signed main () { int t; cin >> t; while (t--) { solve (); } return 0 ;
B. Brightness Begins
输入k,假设一开始有n盏灯,全部是开的状态,即\(a[i]=1,1\leq i\leq n\) ,从\(1\sim n\) 进行遍历,如果\(a[i]\) 可以被i整除,就改变状态(开边关,关变开),要求输出n,保证n中有k盏灯亮着
我们不难发现,
如果i是素数,那么i只能被1和i整除,那么最后的状态一定是开
如果i的因子个数为偶数,那么最后的状态也是开
也就是说,除了i的因子个数为奇数的,最后状态都是开;而因子个数为奇数的数一定是平方数,而平方数的分布有规律性:1,4,9,16,...依次隔了3,5,7,...所以可以按照3,5,7,...进行分组,除了第一个数状态是0,其他都是1,然后用二分查找即可
注意二分查找的边界条件!!!!!
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 #include <iostream> #define int long long using namespace std;int sum (int x) { return (3 + (2 * x + 1 )) * x / 2 ; } int sum1 (int x) { return sum (x) - x; } void solve () { int k; cin >> k; int l = 0 , r = 1e9 , mid; while (l < r) { mid = l + r + 1 >> 1 ; int t = sum1 (mid); if (t > k) r = mid - 1 ; else if (t < k) l = mid; else { cout << sum (mid) << '\n' ; return ; } } cout << sum (l) + k - sum1 (l) + 1 << '\n' ; } signed main () { int t; cin >> t; while (t--) { solve (); } return 0 ; }
C. Bitwise Balancing
给定三个整数b,c,d,要求求出一个a,让\((a|d)-(a\&c)=d\)
直接列举a的每一个二进制位即可
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 #include <iostream> #include <cmath> #define int long long using namespace std;const int N = 100 ;void solve () { int cnt = 0 , a[N]; int b, c, d; cin >> b >> c >> d; while (b || c || d) { int t1 = b & 1 , t2 = c & 1 ; int ans1 = t1, ans2 = 1 - t2; if (ans2 == (d & 1 )) a[cnt++] = 1 ; else if (ans1 == (d & 1 )) a[cnt++] = 0 ; else { cout << -1 << '\n' ; return ; } b >>= 1 , c >>= 1 , d >>= 1 ; } int ans = 0 ; for (int i = 0 ; i < cnt; i++) if (a[i]) ans += pow (2 , i); cout << ans << '\n' ; } signed main () { int t; cin >> t; while (t--) { solve (); } return 0 ; }
D. Connect the Dots
输入三个整数a,d,k,输入m条指令,每次将\(a,a+d,a+2d,...,a+kd\) 连接起来,问最后有多少个联通块,\(1\leq k\leq 10\)
一开始我没有注意到k的特殊性,直接用并查集做,然后TLE了;然后看了题解,它是巧妙地应用了一个数组,\(ma[i][j]\) ,表示以i为起点每次跳跃j步的次数,将同一个步数的进行归类,每跳一次步数减1
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 #include <iostream> #include <set> #define int long long using namespace std;const int N = 2e5 + 10 ;int p[N], ma[N][15 ];int find (int x) { if (x != p[x]) p[x] = find (p[x]); return p[x]; } void solve () { int n, m; cin >> n >> m; for (int i = 1 ; i <= n; i++) { p[i] = i; for (int j = 1 ; j <= 10 ; j++) ma[i][j] = 0 ; } while (m--) { int a, d, k; cin >> a >> d >> k; ma[a][d] = max (ma[a][d], k); } for (int i = 1 ; i <= n; i++) { for (int j = 1 ; j <= 10 ; j++) { if (i - j < 1 ) break ; if (ma[i - j][j]) p[find (i - j)] = find (i); } for (int j = 1 ; j <= 10 ; j++) { if (ma[i][j] > 1 && i + j <= n) ma[i + j][j] = max (ma[i + j][j], ma[i][j] - 1 ); } } set<int > s; for (int i = 1 ; i <= n; i++) s.emplace (find (i)); cout << s.size () << '\n' ; } signed main () { int t; cin >> t; while (t--) { solve (); } return 0 ; }