Colorful Slimes 2
贪心。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
int last = -1, answer = 0;
for (int i = 0; i < n; ++i) {
int x;
scanf("%d", &x);
if (x == last) {
++answer;
last = -1;
} else {
last = x;
}
}
printf("%d\n", answer);
return 0;
}
rng_10s
先特判 和 的情况,否则就是初始有 个罐头,之后每次减去 或者加上 。注意到罐头数量落在区间 内,并且它在模 意义下与 同余,那么找最小的那个判断是否大于等于 即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool check(ll a, ll b, ll c, ll d) {
if (a < b || d < b) {
return false;
}
ll g = __gcd(b, d), t = a % g - g;
return t < c - b + 1;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int T;
scanf("%d", &T);
while (T--) {
ll a, b, c, d;
scanf("%lld %lld %lld %lld", &a, &b, &c, &d);
puts(check(a, b, c, d) ? "Yes" : "No");
}
return 0;
}
String Coloring
折半之后随便算算就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
cin >> n;
string s;
cin >> s;
map<pair<string, string>, int> number;
function<void(int, string, string)> dfs = [&](int x, string red, string blue) {
if (x == n) {
++number[make_pair(red, blue)];
return;
}
dfs(x + 1, red + s[x], blue);
dfs(x + 1, red, s[x] + blue);
};
dfs(0, "", "");
reverse(s.begin(), s.end());
ll answer = 0;
function<void(int, string, string)> rec = [&](int x, string red, string blue) {
if (x == n) {
answer += number[make_pair(red, blue)];
return;
}
rec(x + 1, red + s[x], blue);
rec(x + 1, red, s[x] + blue);
};
rec(0, "", "");
cout << answer << endl;
return 0;
}
Histogram Coloring
考虑相邻两行(都是满的),它们要么对应位置颜色都相同,要么都不同,相同的还需要满足两行是两种颜色交错。按照高度来DP算方案数就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int add(int x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
return x;
}
int sub(int x, int y) {
x -= y;
if (x < 0) {
x += mod;
}
return x;
}
int mul(int x, int y) {
return (ll)x * y % mod;
}
int power(int x, int y) {
int result = 1;
for (; y; y >>= 1, x = mul(x, x)) {
if (y & 1) {
result = mul(result, x);
}
}
return result;
}
pair<int, int> solve(int l, int r, int base, vector<int> &a) {
int x = *min_element(a.begin() + l, a.begin() + r), coef = 1, all = 1, number = 0;
for (int i = l; i < r; ++i) {
if (a[i] != x) {
int j = i;
while (j + 1 < r && a[j + 1] != x) {
++j;
}
pair<int, int> value = solve(i, j + 1, x, a);
i = j;
coef = mul(coef, value.first);
all = mul(all, add(value.first, value.second));
} else {
++number;
}
}
return make_pair(mul(coef, power(2, x - base)), add(mul(all, power(2, number)), mul(coef, sub(power(2, x - base), 2))));
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
printf("%d\n", solve(0, n, 0, a).second);
return 0;
}
Synchronized Subsequence
假设 第 次出现的位置是 , 同理,考虑将序列划分成尽量多段,每一段 个数相同,那么在同一段中,要么 ,要么 。
的情况。答案一定是 的形式,否则有连续的两个 ,去掉一个肯定更优。
的情况,一定是选择了一个后缀,因为串是 的形式,如果 选了 一定会选,枚举选哪个后缀取最大的。
可以证明,对于每一段,要么不选要么就选最优的串,剩下的做个DP就可以了。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
cin >> n;
string s;
cin >> s;
vector<int> sum(n << 1 | 1, 0);
for (int i = 0; i < n << 1; ++i) {
sum[i + 1] = sum[i] + (s[i] == 'a' ? 1 : -1);
}
int last = 0;
vector<string> all;
function<string(int, int)> get = [&](int l, int r) {
vector<int> a, b;
for (int i = l; i < r; ++i) {
if (s[i] == 'a') {
a.push_back(i);
} else {
b.push_back(i);
}
}
vector<bool> choose(r - l, false);
if (a[0] < b[0]) {
int last = -1;
for (int i = 0; i < r - l >> 1; ++i) {
if (a[i] > last) {
choose[a[i] - l] = choose[b[i] - l] = true;
last = b[i];
}
}
string result = "";
for (int i = 0; i < r - l; ++i) {
if (choose[i]) {
result += s[i + l];
}
}
return result;
} else {
string result = "";
for (int i = (r - l >> 1) - 1; ~i; --i) {
choose[a[i] - l] = choose[b[i] - l] = true;
string current = "";
for (int j = 0; j < r - l; ++j) {
if (choose[j]) {
current += s[j + l];
}
}
result = max(result, current);
}
return result;
}
};
for (int i = 1; i <= n << 1; ++i) {
if (!sum[i]) {
all.push_back(get(last, i));
last = i;
}
}
vector<string> dp(all.size() + 1, "");
for (int i = all.size() - 1; ~i; --i) {
dp[i] = max(dp[i + 1], all[i] + dp[i + 1]);
}
cout << dp[0] << endl;
return 0;
}
Manju Game
将盒子黑白染色,第一个盒子是黑色。记黑色盒子总和是 ,白色盒子总和是 。
- 是偶数的情况。不难证明先手可以获得至少 ,而后手至少可以获得 。
- 是奇数的情况。如果先手取一个白色盒子,那么后手会从两边中去掉一边变成一个子问题。如果先手拿了黑色盒子,那么先手可以保证 的收益而后手可以保证 的收益。
先手决策的过程可以抽象成一棵二叉树:根是先手第一次选的白色盒子,左右儿子分别是两边子问题中先手选的白色盒子。如果先手选黑色盒子,那么这个点不存在。
如果已知先手的决策树,可以发现先手会得到树上所有白色盒子的收益,这些盒子将原序列分成若干段,而后手可以决策让先手在这些段中的其中一段获得所有黑盒子,后手获得这一段中的白盒子。其他段中,黑盒子归后手,白盒子归先手。
考虑二分答案,检验先手能不能获得高于 的收益,不难发现就是询问是否存在一种用白盒子来分段的方案,使每一段黑盒子减去白盒子的差都大于 ,这个显然贪心就行了。
#include <bits/stdc++.h>
using namespace std;
bool check(vector<int> &a, int mid) {
int value = a[0];
for (int i = 2; i < a.size(); i += 2) {
if (value >= mid) {
value = max(value + a[i] - a[i - 1], a[i]);
} else {
value += a[i] - a[i - 1];
}
}
return value >= mid;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
int sum = 0, black = 0, white = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
sum += a[i];
if (i & 1) {
white += a[i];
} else {
black += a[i];
}
}
if (!(n & 1)) {
printf("%d %d\n", max(black, white), min(black, white));
} else {
int l = 0, r = black, result = 0;
while (l <= r) {
int mid = l + r >> 1;
if (check(a, mid)) {
result = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
printf("%d %d\n", white + result, black - result);
}
return 0;
}