A. 夹心饼干
点击查看代码
void solve() {std::string s;std::cin >> s;if (s[0] == s.back()) {std::cout << "YES\n";} else {std::cout << "NO\n";}
}
B. 食堂大作战1.0 && C. 食堂大作战2.0
题意:有\(n\)个队伍,每个队伍有\(a_i\)人,你要到每个队伍前面各去一次,每时刻每个队伍人数都减\(1\),当你在这个队伍且队伍只有你一个人时你就到了这个队伍前面,然后你可以瞬移到另一个队伍后面。问能否可行以及给出方案。
不算自己的情况,如果一个队伍人数为\(0\)了,那么我们应该在之前排到了这个队伍后面。那么显然如果有两个队伍同时为\(0\),我们无法兼顾这两个队伍。于是每个\(a_i\)只能出现一次。如果每个\(a_i\)只出现一次,那么我们显然可以按从小到大的顺序操作。
点击查看代码
void solve() {int n;std::cin >> n;std::vector<std::pair<int, int>> a(n);for (int i = 0; i < n; ++ i) {int x;std::cin >> x;a[i] = {x, i};}std::sort(a.begin(), a.end());for (int i = 0; i + 1 < n; ++ i) {if (a[i].first == a[i + 1].first) {std::cout << "NO\n";return;}}std::cout << "YES\n";for (int i = 0; i < n; ++ i) {std::cout << a[i].second + 1 << " \n"[i == n - 1];}
}
D. 小苯的排列计数
题意:给出一个排列的前缀\(min\)数组,求有多少排列满足要求。
发现每次数字变化就代表这个数在这个位置上,那么我们从前往后遍历,找每一段相同的,这一段只有一个是固定的,其他只需要填大于这个数的数就可以了,假设\([i, j]\)都是相同的,那么\(p_i = a_i\),\([i + 1, j]\)的位置选比\(a_i\)大的数就行,不过之前以及填过了\(i-1\)个大于\(a_i\)的数,那么我们总共有\(n - a_i - (i - 1)\)个数可以选,总共有\(j - i\)个位置,用组合数求即可,注意每个数可以任意排列,于是还要乘上一个\(j-i\)的排列。
(代码使用了jiangly的取模类以及组合数板子)
点击查看代码
void solve() {int n;std::cin >> n;std::vector<int> a(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}for (int i = 1; i < n; ++ i) {if (a[i] > a[i - 1]) {std::cout << 0 << "\n";return;}}Z ans = 1;for (int i = 0; i < n; ++ i) {int j = i;while (j + 1 < n && a[i] == a[j + 1]) {++ j;}int tot = n - a[i] - i;ans *= comb.binom(tot, j - i) * comb.fac(j - i);i = j;}std::cout << ans << "\n";
}
E. 和+和
题意:给你两个数组\(a, b\),你要在\(a\)里选\(m\)个数,在\(b\)里选\(m\)个数,满足\(a\)里选的数的下标都小于\(b\)里选的数的下标,使得选出数的总和最小。
用优先队列预处理出\([1, i]\)中从\(a\)里选\(m\)个元素的最小值,和\([i, n]\)从\(b\)里选\(m\)个数的最小值。那么就可以枚举\(i\),取两边的值相加。
点击查看代码
void solve() {int n, m;std::cin >> n >> m;std::vector<int> a(n), b(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}for (int i = 0; i < n; ++ i) {std::cin >> b[i];}const i64 inf = 1e18;std::priority_queue<int> heap;i64 sum = 0;std::vector<i64> pre(n, inf), suf(n, inf);for (int i = 0; i < n; ++ i) {if (heap.size() == m) {if (a[i] < heap.top()) {sum -= heap.top();heap.pop();sum += a[i];heap.push(a[i]);}} else {heap.push(a[i]);sum += a[i];}if (heap.size() == m) {pre[i] = sum;}}while (heap.size()) {heap.pop();}sum = 0;for (int i = n - 1; i >= 0; -- i) {if (heap.size() == m) {if (b[i] < heap.top()) {sum -= heap.top();heap.pop();sum += b[i];heap.push(b[i]);}} else {heap.push(b[i]);sum += b[i];}if (heap.size() == m) {suf[i] = sum;}}i64 ans = inf;for (int i = 0; i + 1 < n; ++ i) {ans = std::min(ans, pre[i] + suf[i + 1]);}std::cout << ans << "\n";
}
F. 怎么写线性SPJ
题意:构造一个长度为\(n\)的数组,满足值域都在\([1, n]\)里,且任意一个子数组都至少有一个数在这个子数组里只出现过一次。求不同数字最少的方案。
手搓一下,长度为\(7\)的答案为\(1,2,1,3,1,2,1\),长度为\(15\)的答案为\(1,2,1,3,1,2,1,4,1,2,1,3,1,2,1\)。发现是一个很对称的数组,并且不同数字只有\(log_{2} n + 1\)个,然后大胆猜这就是最小的种类数,于是就过了。
关于代码实现,可以用递归。也可以发现每个相同数之间的距离是固定了,两个\(x\)之间隔了\(2^x\)的距离。
点击查看代码
void solve() {int n;std::cin >> n;std::vector<int> ans(n);int x = 0;for (int i = 0; i < n; ++ i) {if (ans[i] == 0) {++ x;for (int j = i; j < n; j += 1 << x) {ans[j] = x;}}}std::cout << x << "\n";for (int i = 0; i < n; ++ i) {std::cout << ans[i] << " \n"[i == n - 1];}
}