Codeforces Round #585 (Div. 2)

传送门

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 ot ={0}),分别计算:

  • ([l,r])的对数,满足(a_lcdot a_{l+1}cdot cdots cdot a_n>0)
  • ([l,r])的对数,满足(a_lcdot 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() << '
';
    for(auto it : ans) {
        cout << it.fi << ' ' << it.se << '
';
    }
    return 0;
}

D. Ticket Game

题意:
(A)(B)玩博弈游戏,规则如下:
现在给出一个由数字给出的串,长度为偶数,可能会有偶数个位上面的值为(?)
现在两个人依次在(?)上面填(0)~(9)的数,一直填到不能填为止。
(A)为先手,若最后串的左边部分数字之和等于右边部分的数字之和,那么(B)赢,否则(A)赢。

思路:
感觉还是分情况考虑一下,首先我们记已知的数中左半部分和为(x),右半部分和为(y);左边有(a)(?),右边有(b)个。

  • (x=y)
    • (a ot ={b}),易知先手必胜;
    • 否则,后手每次根据先手填满(9),后手必胜。
  • (x ot ={y}),不妨(x>y):
    • (ageq 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] << '
';
}
 
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;
}
原文地址:https://www.cnblogs.com/heyuhhh/p/11530744.html