SDCPC 2019 山东省赛
A. Sekiro
题目大意
初始值为n,每次操作会变为\(\left\lceil \frac{n}{2} \right\rceil\),问操作k次后会变成多少
解题思路
按题意模拟直到n为1
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, k;
std::cin >> n >> k;
while (k-- && n > 1) {
n -= n / 2;
}
std::cout << n << "\n";
}
}
M. Calandar
题目大意
定义一周5天,一个月30天,给你一个日期并告诉你这天是星期几,问另外一个日期是星期几
解题思路
计算日期之差偏移映射即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
std::map<std::string, int> mp1 = {{"Monday", 1}, {"Tuesday", 2}, {"Wednesday", 3}, {"Thursday", 4}, {"Friday", 5}};
std::map<int, std::string> mp2 = {{1, "Monday"}, {2, "Tuesday"}, {3, "Wednesday"}, {4, "Thursday"}, {0, "Friday"}};
while (tt--) {
i64 y1, m1, d1;
std::string s;
std::cin >> y1 >> m1 >> d1 >> s;
i64 y2, m2, d2;
std::cin >> y2 >> m2 >> d2;
i64 cnt1 = 12 * 30 * y1 + 30 * m1 + d1, cnt2 = 12 * 30 * y2 + 30 * m2 + d2;
std::cout << mp2[((cnt2 - cnt1) % 5 + mp1[s] + 5) % 5] << "\n";
}
}
D. Stones in the Bucket
题目大意
给定一个数组,每次操作可以让\(a_i\)-1,也可以让\(a_i\)-1然后\(a_j\)+1,问至少要操作多少次才能让所有数字相等
解题思路
先让总数是n的倍数,然后加上大于平均的即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
i64 n, ave = 0, ans = 0;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
ave += a[i];
}
ave /= n;
for (auto x : a) {
if (x > ave) {
ans += x - ave;
}
}
std::cout << ans << "\n";
}
}
H. Wandering Robot
题目大意
机器人初始坐标为(0,0),给你一个操作序列以及重复次数,问机器人距离原点最远的曼哈顿距离是多少
解题思路
第一次模拟操作序列,之后按照执行倍数直接计算即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
i64 n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
i64 x = 0, y = 0, ans = 0;
std::vector<std::array<i64, 2>> pre(n);
for (int i = 0; i < n; i++) {
if (s[i] == 'U') {
y++;
}
if (s[i] == 'D') {
y--;
}
if (s[i] == 'L') {
x--;
}
if (s[i] == 'R') {
x++;
}
pre[i] = {x, y};
ans = std::max(ans, std::abs(x) + std::abs(y));
}
for (int i = 0; i < n; i++) {
auto [X, Y] = pre[i];
ans = std::max(ans, std::abs((k - 1) * x + X) + std::abs((k - 1) * y + Y));
}
std::cout << ans << "\n";
}
}
F. Game on a Graph
题目大意
给定n点m边的一张图,两组人进行游戏没人轮流删边,有人删完之后图不再联通则该组人输掉游戏,问哪组可以赢得比赛
解题思路
显然删成一棵树的时候再删就会输掉比赛,看这次是哪组人删边就行
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int k;
std::cin >> k;
std::string s;
std::cin >> s;
int n, m;
std::cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
std::cin >> u >> v;
}
int ans = s[(m - n + 1) % k] - '0';
if (ans == 1) {
std::cout << 2 << "\n";
} else {
std::cout << 1 << "\n";
}
}
}
C. Tokens on the Segments
题目大意
给你n条线段,问有多少个线段能放至少一个令牌,令牌放置要求x互不相等
解题思路
如果有多条线段可以放令牌,显然应该选择右端点最小的线段来放,对线段lr排序之后用优先队列模拟即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, x = -1, ans = 0;
std::cin >> n;
std::set<int> st;
std::vector<std::array<int, 2>> lr(n);
for (int i = 0; i < n; i++) {
std::cin >> lr[i][0] >> lr[i][1];
}
std::sort(lr.begin(), lr.end());
x = lr[0][0];
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
for (auto [l, r] : lr) {
if (l == x) {
pq.push(r);
} else {
while (!pq.empty()) {
if (x == l) {
break;
}
if (x <= pq.top()) {
ans++;
pq.pop();
x++;
} else {
pq.pop();
}
}
if (pq.empty()) {
x = l;
}
pq.push(r);
}
}
while (!pq.empty()) {
if (x <= pq.top()) {
ans++;
pq.pop();
x++;
} else {
pq.pop();
}
}
std::cout << ans << "\n";
}
}
B. Median
题目大意
给你m个位置对ab,保证第a个数大于第b个数,问第1~n个数字有没有可能是数组的中位数,保证n是奇数
解题思路
对ab建有向图,然后遍历每个位置搜索看能有多少个数字和当前位置的大小关系是已知的,均不超过一半则当前可能是中位数
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, m, f = 1;
std::cin >> n >> m;
std::vector<std::vector<int>> g(n + 1, std::vector<int>(n + 1));
for (int i = 0; i < m; i++) {
int u, v;
std::cin >> u >> v;
g[u][v] = 1;
}
std::string ans;
for (int i = 1; i <= n; i++) {
std::queue<int> q1, q2;
int cnt1 = 0, cnt2 = 0;
std::vector<int> vis1(n + 1), vis2(n + 1);
for (int j = 1; j <= n; j++) {
if (g[i][j]) { // 第i个比第j个大
q1.push(j);
cnt1++;
vis1[j] = 1;
}
if (g[j][i]) { // 第i个比第j个小
q2.push(j);
cnt2++;
vis2[j] = 1;
}
}
while (!q1.empty()) {
int x = q1.front();
q1.pop();
if (x == i) {
f = 0;
break;
}
for (int j = 1; j <= n; j++) {
if (g[x][j] && !vis1[j]) { // 第x个比第j个大
q1.push(j);
cnt1++;
vis1[j] = 1;
}
}
}
while (!q2.empty()) {
int x = q2.front();
q2.pop();
if (x == i) {
f = 0;
break;
}
for (int j = 1; j <= n; j++) {
if (g[j][x] && !vis2[j]) { // 第x个比第j个小
q2.push(j);
cnt2++;
vis2[j] = 1;
}
}
}
if (cnt1 <= n / 2 && cnt2 <= n / 2) {
ans += '1';
} else {
ans += '0';
}
}
if (f) {
std::cout << ans << "\n";
} else {
std::cout << std::string(n, '0') << "\n";
}
}
}
L. Flipping Game
题目大意
n盏灯,每次可以反转m盏灯的状态,问有多少种方法让k次操作后灯从初始态变为目标态,对答案模998244353
解题思路
三层循环分别枚举操作轮数,状态匹配数,操作后状态匹配数,dp维护,具体操作见代码和注释
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int N = 1e2 + 10;
const int MOD = 998244353;
std::vector<i64> fac(N + 1, 1), invfac(N + 1, 1);
i64 ksm(i64 a, i64 n, i64 mod) {
i64 res = 1;
a = (a % mod + mod) % mod;
while (n) {
if (n & 1) {
res = (a * res) % mod;
}
a = (a * a) % mod;
n >>= 1;
}
return res;
}
void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = fac[i - 1] * i % MOD;
}
invfac[n] = ksm(fac[n], MOD - 2, MOD);
for (int i = n - 1; i >= 0; i--) {
invfac[i] = invfac[i + 1] * (i + 1) % MOD;
}
}
i64 C(int n, int m) { // 组合数
if (m > n || m < 0) {
return 0;
}
return fac[n] * invfac[m] % MOD * invfac[n - m] % MOD;
}
i64 A(int n, int m) { // 排列数
if (m > n || m < 0) {
return 0;
}
return fac[n] * invfac[n - m] % MOD;
}
// n 对括号的合法匹配数,有 n 个节点的二叉树的种类数
// 从对角线下方走到对角线的路径数,栈的出栈序列数
i64 catalan(int n) { // 卡特兰数
if (n < 0) {
return 0;
}
return C(2 * n, n) * ksm(n + 1, MOD - 2, MOD) % MOD;
}
// 将 n 个不同的元素划分到 k 个非空集合中的方案数
i64 stirling2(int n, int k) { // 第二类斯特林数
if (k > n || k < 0) {
return 0;
}
i64 res = 0;
for (int i = 0; i <= k; i++) {
i64 term = C(k, i) * ksm(k - i, n, MOD) % MOD;
if (i % 2 == 1) {
term = (MOD - term) % MOD;
}
res = (res + term) % MOD;
}
return res * invfac[k] % MOD;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
init(N);
int tt;
std::cin >> tt;
while (tt--) {
int n, k, m, cnt = 0;
std::cin >> n >> k >> m;
std::string s, t;
std::cin >> s >> t;
std::vector<std::vector<i64>> dp(k + 1, std::vector<i64>(n + 1));
for (int i = 0; i < n; i++) {
cnt += s[i] != t[i];
}
dp[0][cnt] = 1;
for (int K = 0; K < k; K++) { // 第K轮
for (int N = 0; N <= n; N++) { // 这轮原本错N个
for (int i = 0; i <= m; i++) { // 改错i个,改对m-i个
int CNT = N + i - (m - i); // 这一次操作会有CNT个错误
if (CNT >= 0 && CNT <= n) {
// n-N个正确里选i个变成不正确,N个不正确里选m-i个改为正确,再乘上上一个状态的方案数
dp[K + 1][CNT] += C(n - N, i) * C(N, m - i) % MOD * dp[K][N] % MOD;
dp[K + 1][CNT] %= MOD;
}
}
}
}
std::cout << dp[k][0] << "\n";+
}
}
E. BaoBao Loves Reading
题目大意
按顺序看书,如果已经在桌子(可以放k本书)上则直接看,否则从书架上取书,当桌上书少于k本时直接取书,否则把桌上最早阅读的书放书架回再取要看的书,问桌子容量为1~n时需要取书的次数是多少。
解题思路
记录每次重复的书出现的位置之间有多少本不一样的书,如果书的数量小于桌子容量则可以减少一次拿书操作,可以用莫队或者树状数组维护
代码实现
// 莫队做法
#include <bits/stdc++.h>
const int N = 1e5 + 10;
std::vector<int> cnt(N), pos(N);
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') {
f = -1;
}
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void write(int x) {
if (x < 0) {
putchar('-'), x = -x;
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt = read();
while (tt--) {
int n = read();
int size = std::sqrt(n);
int bnum = ceil((double)n / size);
std::vector<int> a(n + 1), block(n + 1);
for (int i = 1; i <= bnum; i++) {
for (int j = (i - 1) * size + 1; j <= i * size && j <= n; j++) {
block[j] = i;
}
}
for (int i = 1; i <= n; i++) {
a[i] = read();
pos[a[i]] = 0;
}
std::vector<std::array<int, 2>> ask;
for (int i = 1; i <= n; i++) {
if (pos[a[i]]) {
ask.push_back({pos[a[i]], i});
}
pos[a[i]] = i;
}
std::sort(ask.begin(), ask.end(), [&](std::array<int, 2> a, std::array<int, 2> b) {
if (block[a[0]] != block[b[0]]) {
return block[a[0]] < block[b[0]];
}
if (block[a[0]] & 1) {
return a[1] < b[1];
} else {
return a[1] > b[1];
}
});
int l = 1, r = 0, now = 0;
std::vector<int> ans(n + 1);
for (auto [L, R] : ask) {
while (l > L) {
l--;
now += !cnt[a[l]];
cnt[a[l]]++;
}
while (r < R) {
r++;
now += !cnt[a[r]];
cnt[a[r]]++;
}
while (l < L) {
cnt[a[l]]--;
now -= !cnt[a[l]];
l++;
}
while (r > R) {
cnt[a[r]]--;
now -= !cnt[a[r]];
r--;
}
ans[now]--;
}
for (int i = l; i <= r; i++) {
cnt[a[i]] = 0;
}
ans[1] += n;
for (int i = 1; i <= n; i++) {
ans[i] += ans[i - 1];
}
for (int i = 1; i <= n; i++) {
write(ans[i]);
putchar(" \n"[i == n]);
}
}
}
// 树状数组做法
#include <bits/stdc++.h>
using i64 = long long;
const int N = 1e5 + 10;
std::vector<int> tree(N), pos(N);
int lowbit(int x) {
return x & (-x);
}
void add(int pos, int x) {
for (int i = pos; i < N; i += lowbit(i)) {
tree[i] += x;
}
}
int ask(int pos) {
int res = 0;
for (int i = pos; i; i -= lowbit(i)) {
res += tree[i];
}
return res;
}
int query(int l, int r) {
return ask(r) - ask(l - 1);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n;
std::cin >> n;
std::vector<int> a(n + 1), ans(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
pos[a[i]] = 0;
}
std::fill(tree.begin(), tree.end(), 0);
for (int i = 1; i <= n; i++) {
add(i, 1);
if (pos[a[i]]) {
add(pos[a[i]], -1);
ans[query(pos[a[i]], i)]--;
}
pos[a[i]] = i;
}
ans[1] += n;
for (int i = 1; i <= n; i++) {
ans[i] += ans[i - 1];
}
for (int i = 1; i <= n; i++) {
std::cout << ans[i] << " \n"[i == n];
}
}
}
K. Happy Equation
题目大意
\(a^x \equiv x^a \pmod{2^p}\),给定ap,求有多少x满足
解题思路
打表发现a为奇数时答案是1(证明),a为偶数时分类讨论,\(x \leq p\)时暴力检查,\(x \geq p\)时找\(x^a\%2^p=0\)其实就是在\(2^p\)里找有多少\(2^{\lceil \frac{p}{a} \rceil}\)再减去p中有多少个\(2^{\lceil \frac{p}{a} \rceil}\)
代码实现
#include <bits/stdc++.h>
using i64 = long long;
i64 ksm(i64 a, i64 n, i64 mod) {
i64 res = 1;
a = (a % mod + mod) % mod;
while (n) {
if (n & 1) {
res = (a * res) % mod;
}
a = (a * a) % mod;
n >>= 1;
}
return res;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
i64 a, p, ans = 0;
std::cin >> a >> p;
if (a % 2) {
std::cout << 1 << "\n";
} else {
for (int x = 0; x <= p; x++) {
ans += ksm(a, x, 1 << p) == ksm(x, a, 1 << p);
}
int t = (1 << (p + a - 1) / a);
ans += (1 << p) / t - p / t;
std::cout << ans << "\n";
}
}
}

浙公网安备 33010602011771号