Codeforces Round #585 (Div. 2)

Posted heyuhhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\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^mcnt[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;

以上是关于Codeforces Round #585 (Div. 2)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #585 (Div. 2) D. Ticket Game

Codeforces Round #585 (Div. 2)

CodeForces Round #585 (Div 2)

Codeforces Round #585 (Div. 2)

Codeforces Round #585 (Div. 2) B.The Number of Products(动态规划)

Codeforces Round #585 (Div. 2) CF1215A~C