赞
踩
比赛链接(含视频题解):https://www.starrycoding.com/contest/4
给你一个由 n n n 个正整数组成的数组 a a a,询问这个数组是否是严格单调递增的。
因为他会按照“拜访时间安排表”的顺序拜访每一位好友,而无法成功拜访某一好友时,必定是出现靠后拜访的时间早于靠前拜访的,或者两次拜访的时间相同,所以问题的本质就是判断数组是否严格单调递增,也就是 a 1 < a 2 < ⋯ < a n a_1 < a_2 < \dots < a_n a1<a2<⋯<an。
我们只需要遍历一下整个数组,判断相邻两个数是否都满足后者大于前者即可。
void solve() { int n; cin >> n; vector<int> a(n + 1, 0); for (int i = 1; i <= n; i++) { cin >> a[i]; } for (int i = 2; i <= n; i++) { if (a[i] <= a[i - 1]) { cout << "NO\n"; return; } } cout << "YES\n"; return; }
由于数的范围很小,假如这个数合法,我们直接从此数 + 1 +1 +1开始枚举,直到合法为止
最坏情况下时间复杂度为 O ( T n / 2 ) O(Tn/2) O(Tn/2)
#include<bits/stdc++.h> #define int long long #define inf 0x3f3f3f3f3f3f3f const int N = 2e5 + 10; const int M = 15; const int mod = 998244353; using namespace std; typedef pair<int, int>p; typedef long long ll; int a(int x) { int res = 0; while (x) { if (x & 1)res++; x >>= 1; } return res; } signed main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while (T--) { int n; cin >> n; if (a(n)<=2) { n++; while (a(n) >= 3)n++; cout << n << endl; } else { cout << a(n) << endl; } } }
假如我们还是一个个枚举对于 2 63 2^{63} 263 + + + 2 62 2^{62} 262这样的数,需要枚举约 2 62 2^{62} 262次,我们是无法接受的
我们考虑在哪一位上加1是可行的
首先对于只有 0 0 0位 1 1 1或者 1 1 1位 1 1 1的情况,我们只需要无脑在第一位上加 1 1 1即可,因为这保证了 1 1 1的数量不会超过要求
对于 2 2 2位 1 1 1的情况:
1 1 1 : 假如我在最后一位 1 1 1的后面任意位增加 1 1 1,一定会使 1 1 1的数量变成 3 3 3,不符合要求
2 2 2 : 假如我在最后一位 1 1 1上加 1 1 1,能保证加的数量是最少的,并且 1 1 1的数量一定不会增加,符合要求
所以对于 2 2 2位1的情况我们加上lowbit(x)就行了
时间复杂度接近 O ( T ) O(T) O(T)
#include<bits/stdc++.h> #define int long long #define inf 0x3f3f3f3f3f3f3f const int N = 2e5 + 10; const int M = 15; const int mod = 998244353; using namespace std; typedef pair<int, int>p; typedef long long ll; int lowbit(int x) { return x & -x; } int a(int x) { int res = 0; while (x) { if (x & 1)res++; x >>= 1; } return res; } signed main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int T = 1; cin >> T; while (T--) { int n; cin >> n; if (a(n) < 3) { if (a(n) <= 1)cout << n + 1 << endl; else { n += lowbit(n); cout << n << endl; } } else { cout << a(n) << endl; } } }
给你一个由 n n n 个正整数组成的数组 a a a,询问这个数组能否通过以下两个操作各至多一次变为严格单调递增:
由于我们已经知道了合法的时间安排满足数组 a a a 严格单调递增,所以我们只需要判断是否能够将 a a a 变为严格单调递增即可。
这个问题可以用简单的贪心法来解决。逐个处理元素。如果某个数比前一个数小,那么肯定不行,我们需要考虑删去这个元素或者它前面的那个数。如果相等,则考虑将其增加 1 1 1。如果大于,则最好不要更改,给后面的元素更多的空间。
最后我们只需要判断是否能通过最多一次删除操作使数组合法即可。
当然,还有一个小细节,当某个数比前一个数小时( a i > a i + 1 a_i > a_{i+1} ai>ai+1),应该删除这个数还是前一个数,因为我们想要的是让后面的数的合法值域更大,所以我们需要最小化留下的那个数,也就是说,如果靠后的数 a i + 1 a_{i+1} ai+1 可以留下来,我们应该尽可能让其留下,也就是说
void solve() { int n, k = 0; cin >> n; vector<int> a(n + 1, 0); for (int i = 1; i <= n; i++) { cin >> a[i]; } int b = a[1]; for (int i = 2; i <= n; i++) { if (a[i] > b) { b = a[i]; continue; } if (a[i] == b) { a[i]++; b = a[i]; } else { k++; if (k == 2) { cout << "NO\n"; return; } if (a[i] < a[i - 2]) { b = a[i - 1]; } else if (a[i] == a[i - 2]) { a[i]++; b = a[i]; } else { b = a[i]; } } } cout << "YES\n"; return; }
观察到
k
k
k并不大,并且是一个比较经典的网格dp
网格dp
一般设:dp[i][j]
表示从(1,1)
点走到(i,j)
点的最大价值/收益等,因题目而异。
在dp
中,一般遇到模数问题,我们可以将模数作为状态保存在数组中。于是我们可以得到
dp[i][j][x]:表示从(1,1)走到(i,j)点切mod k = x的方案数
我们一开始就在(1,1)点,即dp[1][1][a[1][1] % k] = 1
状态转移如下
dp[i + 1][j][x * a[i + 1][j] % k] += dp[i][j] // 向下走
dp[i][j + 1][x * a[i][j + 1] % k] += dp[i][j] // 向右走
显然,最后的答案是:dp[n][n][0]
。
AC Code
const int MOD = 998244353, N = 510; int a[N][N]; long long dp[N][N][30]; void solve(){ int n, k; std::cin >> n >> k; for(int i = 1; i <= n; i ++ ){ for(int j = 1; j <= n; j ++ ){ std::cin >> a[i][j]; } } for(int i = 1; i <= n; i ++ ){ for(int j = 1; j <= n; j ++ ){ if(i == 1 && j == 1){ dp[1][1][a[1][1] % k] = 1; } for(int x = 0; x < 20; x ++ ){ dp[i + 1][j][a[i + 1][j] * x % k] += dp[i][j][x]; dp[i + 1][j][a[i + 1][j] * x % k] %= MOD; dp[i][j + 1][a[i][j + 1] * x % k] += dp[i][j][x]; dp[i][j + 1][a[i][j + 1] * x % k] %= MOD; } } } std::cout << dp[n][n][0]; }
StarryCoding是面向计算机专业学生的综合学习与刷题平台,欢迎同学们的加入!
www.starrycoding.com
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。