牛客周赛 Round 82
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];
}
}

浙公网安备 33010602011771号