A. Yellow Cards
细心点即可。
Code
#include <bits/stdc++.h>
#define fi first
#define se second
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int a1, a2, k1, k2;
int n;
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> a1 >> a2 >> k1 >> k2;
cin >> n;
int t = (k1 - 1) * a1 + (k2 - 1) * a2;
int Min = max(0, n - t);
int Max = 0;
if(k1 > k2) {
int out = min(n / k2, a2);
n -= out * k2;
Max = out;
if(n >= k1) {
Max += min(n / k1, a1);
}
} else {
int out = min(n / k1, a1);
n -= out * k1;
Max = out;
if(n >= k2) {
Max += min(n / k2, a2);
}
}
cout << Min << ' ' << Max;
return 0;
}
B. The Number of Products
题意:
给出序列\(a_1,a_2,\cdots,a_n,a_i\not ={0}\),分别计算:
- \([l,r]\)的对数,满足\(a_l\cdot a_{l+1}\cdot \cdots \cdot a_n>0\);
- \([l,r]\)的对数,满足\(a_l\cdot a_{l+1}\cdot \cdots \cdot a_n<0\);
思路:
- 显然最终结果与\(a_i\)的正负有关。
- 我们将负数变为\(1\),正数变为\(0\),那么我们可以看作求区间和为奇数/偶数的区间个数。
- 维护一个前缀和,再拿个桶统计个数即可。
详见代码:
Code
#include <bits/stdc++.h>
#define fi first
#define se second
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int n;
int a[N], sum[N];
ll cnt[2], ans[2];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
if(a[i] > 0) a[i] = 0;
else a[i] = 1;
}
for(int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + a[i];
}
for(int i = 1; i <= n; i++) sum[i] %= 2;
++cnt[0];
for(int i = 1; i <= n; i++) {
if(sum[i]) {
ans[0] += cnt[1];
ans[1] += cnt[0];
} else {
ans[0] += cnt[0];
ans[1] += cnt[1];
}
++cnt[sum[i]];
}
cout << ans[1] << ' ' << ans[0];
return 0;
}
C. Swap Letters
题意:
给出两个只由\(a,b\)组成的字符串。
现在可以执行操作:在\(s\)中选择一个\(pos_1\),在\(t\)中选择一个\(pos_2\),然后交换\(s_{pos_1},t_{pos_2}\)。
求使得\(s=t\)的最小操作次数,或回答不能使其相等。
思路:
我们可以分情况考虑:
- 对于\(s_i=t_i\)的位置,我们不用管;
- 之后将\(s_i=a,t_i=b\)的所有位置拿出来,将这样的对记作\((a,b)\);同理,另外一种情况记作\((b,a)\)。
- 观察可以发现:显然要先将所有属于相同类型的对两两匹配,这样每次只需要操作一次。
- 最后会剩下奇数个数的对数,若只存在一个\((a,b)\)和一个\((b,a)\),此时需要两次操作;否则就不能使其相等。
简单来说,就是分情况考虑就行了。
Code
#include <bits/stdc++.h>
#define fi first
#define se second
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int n;
char s[N], t[N];
int cnt[4];
bool used[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
cin >> s + 1 >> t + 1;
vector <pii> v;
for(int i = 1; i <= n; i++) {
if(s[i] == t[i]) continue;
if(s[i] == 'a' && t[i] == 'b') {
v.push_back(MP(0, i));
} else v.push_back(MP(1,i));
}
sort(v.begin(), v.end());
vector <pii> ans;
for(int i = 1; i < v.size();) {
if(v[i].fi == v[i - 1].fi) {
ans.push_back(MP(v[i - 1].se, v[i].se));
used[v[i - 1].se] = used[v[i].se] = 1;
i += 2;
} else ++i;
}
vector <pii> t;
for(auto it : v) {
if(!used[it.se]) t.push_back(it);
}
if((int)t.size() & 1) {
cout << -1; return 0;
}
for(int i = 1; i < t.size(); i += 2) {
ans.push_back(MP(t[i].se, t[i].se));
ans.push_back(MP(t[i - 1].se, t[i].se));
}
cout << ans.size() << '\n';
for(auto it : ans) {
cout << it.fi << ' ' << it.se << '\n';
}
return 0;
}
D. Ticket Game
题意:
\(A\)和\(B\)玩博弈游戏,规则如下:
现在给出一个由数字给出的串,长度为偶数,可能会有偶数个位上面的值为\(?\)。
现在两个人依次在\(?\)上面填\(0\)~\(9\)的数,一直填到不能填为止。
\(A\)为先手,若最后串的左边部分数字之和等于右边部分的数字之和,那么\(B\)赢,否则\(A\)赢。
思路:
感觉还是分情况考虑一下,首先我们记已知的数中左半部分和为\(x\),右半部分和为\(y\);左边有\(a\)个\(?\),右边有\(b\)个。
- 若\(x=y\):
- 若\(a\not ={b}\),易知先手必胜;
- 否则,后手每次根据先手填满\(9\),后手必胜。
- 若\(x\not ={y}\),不妨\(x>y\):
- 若\(a\geq b\),易知先手必胜,因为先手会在一边不断放\(9\),后手只能不断维持差距,但最终不能弥补上差距;
- 若\(a<b\),前面的\(2a\)回合策略肯定同上,先手不断放\(9\),后手则维持平衡;在剩下的回合中,当先手放\(x\)时,后手唯一优势就是能放一个\(y\),使得\(x+y=9\)。所以当\(9|x-y\)时,后手必胜;否则,先手放一个较大的数,后面后手没放一个\(y\),先手就放一个\(x\)使得\(x+y=9\),那么后手就GG了。
代码如下:
Code
#include <bits/stdc++.h>
#define fi first
#define se second
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int n;
char s[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
cin >> s + 1;
int x = 0, y = 0, a = 0, b = 0;
for(int i = 1; i <= n; i++) {
if(s[i] != '?') {
if(i <= n / 2) x += s[i] - '0';
else y += s[i] - '0';
} else {
if(i <= n / 2) ++a;
else ++b;
}
}
if(x < y) swap(x, y), swap(a, b);
if(a == b) {
if(x == y) cout << "Bicarp";
else cout << "Monocarp";
} else {
if(a > b) {
cout << "Monocarp";
} else {
int d = x - y;
int t = (b - a) / 2;
if(9 * t == d) {
cout << "Bicarp";
} else cout << "Monocarp";
}
}
return 0;
}
E. Marbles
题意:
给出\(n\)个数,每个数大小不超过\(20\)。
现在可以执行操作:任意交换两个相邻的数。
现要求最少的次数使得颜色相同的数在同一块中。
思路:
- 注意到每个数大小不超过\(20\),那么我们可以考虑对权值来状压。
- 我们首先维护一个\(cnt[i,j]\),表示将\(i\)类颜色放在\(j\)类后面所需最小代价,注意,此时我们只考虑\(i,j\)两种颜色,不考虑其余的颜色。
- 之后我们枚举状态\(s\),表示某些颜色被分为一块的最小代价。之后枚举一个新的颜色\(i\)加进去,假设\(j_1,j_2,\cdots,j_m\)的颜色已经分成一块了,代价就加上\(\sum_{k=1}^{m}cnt[i,j_k]\),最后取\(min\)即可。
做法就说完了,感觉并不是很难。我觉得这个题巧妙的地方就是预处理的这个\(cnt\)数组。
预处理的时候只考虑了两个数,为什么?
这其实就相当于将\(n^2\)个关系一一拆开,假设\(i,j\)中间还有一个颜色\(k\),我们一开始考虑时不会算上\(k\),\(k\)的贡献我们会在最后算上。因为无论哪一次交换,都被包含在这\(n^2\)个关系中。
虽然中间结果可能不正确,最后一定会覆盖到正确的情况,就类似于曼哈顿距离拆绝对值进行枚举,虽然中间结果不一定是正确的,但最终答案肯定能够覆盖到。
Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 4e5 + 5, MAX = 1 << 20;
int n;
int a[N];
ll cnt[20][20];
vector <int> v[20];
ll dp[MAX];
void run() {
for(int i = 1; i <= n; i++) {
cin >> a[i], --a[i];
}
for(int i = 0; i < 20; i++) {
v[i].clear();
for(int j = 0; j < 20; j++)
cnt[i][j] = cnt[j][i] = 0;
}
for(int i = 1; i <= n; i++) {
v[a[i]].push_back(i);
}
for(int i = 0; i < 20; i++) {
if(!sz(v[i])) continue;
for(int j = 0; j < 20; j++) {
if(!sz(v[j]) || i == j) continue;
int pos2 = 0;
for(int pos1 = 0; pos1 < sz(v[i]); pos1++) {
while(pos2 < sz(v[j])) {
if(v[j][pos2] > v[i][pos1]) break;
++pos2;
}
cnt[i][j] += sz(v[j]) - pos2;
}
}
}
int lim = 1 << 20;
for(int i = 0; i < lim; i++) dp[i] = INF;
for(int i = 0; i < 20; i++) dp[1 << i] = 0;
for(int s = 0; s < lim; s++) {
vector <int> was;
for(int i = 0; i < 20; i++) {
if(s >> i & 1) was.push_back(i);
}
for(int i = 0; i < 20; i++) {
ll sum = 0;
if((s >> i & 1) == 0) {
for(auto j : was) {
sum += cnt[i][j];
}
}
int ns = (s ^ (1 << i));
dp[ns] = min(dp[ns], dp[s] + sum);
}
}
cout << dp[lim - 1] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
while(cin >> n) run();
return 0;
}