牛客 周赛106 20250904
https://ac.nowcoder.com/acm/contest/116002
A:
题目大意:
void solve(){int n;cin >> n;if (n & 1) cout << "NO" << '\n';else cout << "YES" << '\n';
}
签到
B:
题目大意:
void solve(){int n, k;cin >> n >> k;auto check = [](int x){int t = x, s = 0;while (t){s *= 10;s += (t % 10);t /= 10;}return s == x;};int cnt = 0;while (!check(n)){cnt ++;if (cnt > k) break;int t = n, s = 0;while (t){s *= 10;s += (t % 10);t /= 10;}n += s; }if (cnt > k) cout << n << ' ' << -1 << '\n';else cout << n << ' ' << cnt << '\n';
}
模拟即可,时间复杂度为 \(O(nk\log x)\)
C:
题目大意:
void solve(){int n, l1, r1, l2, r2;cin >> n >> l1 >> r1 >> l2 >> r2;vector<int> a(n + 1);for (int i = 3; i <= n; i ++) cin >> a[i];for (int i = l1; i <= min(r1, l1 + 9); i ++){for (int j = l2; j <= min(r2, l2 + 9); j ++){a[1] = i % 10, a[2] = j % 10;bool f = 1;for (int k = 3; k <= n; k ++){if (a[k] != (a[k - 1] * a[k - 2]) % 10){f = 0;break;}}if (f){cout << i << ' ' << j <<'\n';return;} }}cout << -1 << ' ' << -1 << '\n';
}
枚举所有个位的情况,按照字典序从小到大循环,时间复杂度为 \(O(100n)\)
D:
题目大意:
void solve(){int n;cin >> n;vector<int> a(n + 1);for (int i = 1; i <= n; i ++) cin >> a[i];vector<int> xr[n + 1];for (int i = 1; i <= n; i ++){int x = a[i];do{xr[i].push_back(x);x ^= (x >> 1);}while(x != a[i]);}int ans = 0;for (int i = 1; i <= n/2; i ++){int j = n - i + 1;int s = 1e9;for (int l = 0; l < xr[i].size(); l ++)if (xr[i][l] == a[j]) s = min(l, (int)xr[i].size() - l);if (s == 1e9){cout << -1 << '\n';return;}ans += s;}cout << ans << '\n';
}
对 \(x\) 进行替换操作相当于把 \(x\) 和他自身右移一位的结果异或起来,这样的操作最多执行 \(\log x\) 次后 \(x\) 复原
所以计算每个元素通过给定的操作可以变成的数字,然后枚举回文的另一个位置上的元素是否也在循环中
for (int l = 0; l < xr[i].size(); l ++)if (xr[i][l] == a[j]) s = min(l, (int)xr[i].size() - l);
选取操作次数最少的方向累加答案,时间复杂度为 \(O(n\log x)\)
证明:
假设 \(x\) 的二进制表示为 \(a_0a_1a_2\cdots a_n\),那么进行一次操作后二进制序列会变为
进行第二次操作会变为
定义 \(f_{i,j}\) 表示第 \(i\) 次操作后二进制序列的第 $j $ 个元素的值,得到递推式
又因为 \(j - 2^k\) 足够小时变为负数,越界后的 \(a_{j-2^k}\) 为前导零,那么 \(f_{i,j}=f_{i-2^k,j}\) 成立
E:
题目大意:
int D[10] = {1, 0, 0, 0, 1, 0, 1, 0, 2, 1};
LL val[25];void init(){val[0] = 1;val[1] = 4;val[2] = 8;for (int i = 3;i <= 22; i ++){if (i & 1) val[i] = val[i - 1] + 4 * pow(10, i >> 1);else val[i] = val[i - 1] + 4 * pow(10, (i >> 1) - 1);}
}void solve(){LL n, sum;cin >> n >> sum;if (sum < n){cout << -1 << '\n'; return;}LL avg = sum / n;LL base;for (int i = 0; i <= 20; i ++){if (val[i + 1] > avg){base = i;break;}}vector<LL> ans;while (sum < val[base + 1] *(n - ans.size())){ans.push_back(val[base]);sum -= val[base];}while (ans.size() < n)ans.push_back(val[base + 1]);for (auto x : ans) cout << x << ' ';cout << '\n';
}
在洞数为 \(i\) 的情况下,我们可以确定出一个值最小的 \(x\) 来满足洞数等于 \(i\)
\(8\) 可以提供两个洞,那么在能填 \(8\) 的情况下先填 \(8\) 一定最优,如果洞数为 \(1\),那么 \(x\) 一定为 \(4\) ,因为 \(0\) 不能在前导位上
所以我们构造的策略是:洞数为偶数,全部都填 \(8\) ,否则就先填一个 \(4\)
val[1] = 4;
val[2] = 8;
val[3] = 48;
val[4] = 88;
....
题目给定的 \(sum\) 和 \(n\) ,计算出每个元素位置上可以填入的平均值 avg
,先填入平均值以下的洞数尽可能大的数
然后当剩余的 \(sum\) 足够填下平均值以上的数时,就填这些数进去
vector<LL> ans;
while (sum < val[base + 1] *(n - ans.size())){ans.push_back(val[base]);sum -= val[base];
}
while (ans.size() < n)ans.push_back(val[base + 1]);
F:
题目大意:
void solve(){int n;cin >> n;vector<int> a(n + 2, 0);for (int i = 1; i <= n; i ++) cin >> a[i];int ans = 0;stack<int> st;for (int i = 1; i <= n; i ++){int f = 0;while (st.size() && st.top() < a[i]){f = 1;st.pop();}if (st.size() && a[i] != st.top() && a[i] < st.top() && f) ans ++; st.push(a[i]);}while (st.size()) st.pop();for (int i = n; i >= 1; i --){int cnt = 0;while (st.size() && st.top() < a[i]){cnt ++;st.pop();}if (st.size() && a[i] != st.top() && a[i] < st.top() && f) ans ++; st.push(a[i]);}cout << ans << '\n';
}
当一段区间满足除左右端点外的元素都小于左右端点的最小值时,这个区间为一个美丽数组
分为两种情况讨论:
-
\(a_l > a_r\) ,类似单调栈的形式从左到右遍历数组,当 \(a_i\) 大于了栈顶元素时,说明 \(a_i\) 是一个可能的右端点
依次弹出栈内小于 \(a_i\) 的元素,如果有栈不为空, \(a_l \ge a_i\) 且弹出的元素至少有 \(1\) 个
if (st.size() && a[i] != st.top() && a[i] < st.top() && f)
说明在区间 \([l,i]\) 的子数组是一个美丽数组
-
\(a_r > a_l\) 同理,从右向左遍历数组即可