Educational Codeforces Round 107 (Rated for Div. 2,CF1511)A~E题解

文章目录

      • A
      • B
      • C
      • D
        • 法一
        • 更好的法二
        • 代码
      • E
        • 实现代码
      • 参考链接

传送门

CSDN传送门

juejin传送门

作者:hans774882968以及hans774882968

A

题意:有三种类型的客人,他们依次到来,一种反对者,一种支持者,还有一种观望者,即根据当前情况,如果反对数 > 支持数,就投反对,反之投支持的人。现在你有两台服务器,你可以选择给当前来到的客人展示其中一台服务器,问你最多可以获得多少票支持。

有两台服务器,所以把所有dislike都集中到一台服务器即可(提纯是吧)。

from collections import defaultdict


for _ in range(int(input())):
    n = int(input())
    a = list(map(int, input().split()))
    print(sum([1 if v == 1 or v == 3 else 0 for v in a]))

B

题意:给三个数a,b,c,让你找到两个数xy,使得xygcd(x,y)这三个数的位数分别是abc

最简单的方法是,令xy都有c-1个0,则只需要求x0y0,使得两者满足位数条件且gcd(x0,y0) = 1。考虑"1" * (a-c+1)"1" + "0" * (b-c),由辗转相除法可知它们确实互质。

from typing import List
from collections import defaultdict


for _ in range(int(input())):
    x, y, z = map(int, input().split())
    print("1" * (x - z + 1) + "0" * (z - 1), "1" + "0" * (y - 1))
    # c = "1" + "0" * (z - 1)

C

题意:从前往后给你n张卡片,每个卡片都有一个颜色,然后有m次询问,每次询问找到最前面的这个颜色的卡片,输出它的下标,然后把它放到最开头去。

看上去是数据结构,其实是很简单的性质题。我们发现,最前面的某颜色卡片放到最开头后,仍然是最前面的。所以不是最前面的卡片都没有意义,只需要维护每种颜色最前面的卡片的下标。故引入a[x]表示颜色x的最靠前面的下标。设当前询问颜色为t,则修改操作是:对a中所有小于a[t]的数加1,然后a[t]变为1。

因为颜色值域才50,所以可直接暴力。

#include 
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 50 + 5;

int n, q, a[N];

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

int main() {
    read (n, q);
    int mx = 0;
    rep (i, 1, n) {
        int x;
        read (x);
        mx = max (mx, x);
        if (!a[x]) a[x] = i;
    }
    rep (i, 1, q) {
        int t;
        read (t);
        printf ("%d%c", a[t], " \n"[i == q]);
        rep (j, 1, mx) if (a[j] < a[t]) a[j]++;
        a[t] = 1;
    }
    return 0;
}

D

题意:让你用从'a'开始的前m个字母,构造一个字符串S(1-indexed),使得满足1<=iS[i]=S[j]S[i+1]=S[j+1]i,j对数最少。

这种构造题我怎么可能会嘛

法一

看了参考链接1,思路是直接贪心地保证当前看上去比较优,即枚举所有候选字母c,选择las + c出现次数最少的(实现上,用一个哈希表,把长度为2的字符串映射成m进制数作为key)。若有多个最少的,应选择最大的字母,因为这样能最大化预留后续的选择空间。这样求出的字符串会是zzyzxzw...的形式,字典序很大。

看第一个样例9 4的答案aabacadbb,对以上方法求出的字符串进行一个简单的映射即可得到。下面的代码使用了那个额外的映射。

更好的法二

法一求出的字符串进行简单映射后,就很有规律,以m = 4为例:aabacadbbcbdccdd。不断复制直到长度大于等于n即可。

代码

法一

#include 
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 3e5 + 5;

int n, m, cnt[26 * 26];
char s[N];

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

int main() {
    read (n, m);
    re_ (i, 0, n) {
        int las = i ? s[i - 1] - 'a' : 0, d = -1, mn_d = 1e9;
        re_ (j, 0, m) {
            if (mn_d >= cnt[26 * las + j]) {
                mn_d = cnt[26 * las + j];
                d = j;
            }
        }
        cnt[26 * las + d]++;
        s[i] = 'a' + d;
    }
    re_ (i, 0, n) s[i] = 'a' + ('a' + m - 1 - s[i]);
    puts (s);
    return 0;
}

法二

from collections import defaultdict


n, m = map(int, input().split())
ans = ""
for i in range(m):
    ans += chr(97 + i)
    for j in range(i + 1, m):
        ans += chr(97 + i) + chr(97 + j)
while len(ans) < n:
    ans += ans
print(ans[:n])

E

题意:n * m矩阵,由o*组成,o可以涂成红或蓝色。多米诺骨牌是一个1 * 2的矩阵,红色格子只能横着放骨牌,蓝色格子只能竖着放骨牌。这样就很容易求一种涂色方案可放置的最大骨牌数,求所有2 ^ w种方案可放置的最大骨牌数的和,模998244353。

首先看一种涂色方案可放置的最大骨牌数,就是红色格子以一个横条为单位,蓝色格子以一个竖条为单位来求,非常简单。这里体现的性质:任意两个横条、任意两个竖条和横条与竖条之间都互不干扰。又注意到矩阵转置一下,竖条就变成横条。因此我们直接求出每个横条的贡献,求和即可。

设一个横条所有涂法的最大骨牌数之和为v(不考虑矩阵的其他部分),则把矩阵其他部分考虑进来后,该横条对答案的贡献为v * 2^tot2^tot表示矩阵的其他部分的涂色方案总数。

最后只需要知道v的求法。横条唯一的特征就是格子数,因此设dp[i]i个格子的横条的所有涂法的最大骨牌数之和。边界:dp[0] = dp[1] = 0。转移:若第i个格子涂蓝色,则该格子作废,取dp[i-1]。若第i个格子涂红色,则:(1)第i-1个格子涂蓝色,取dp[i-2]。(2)第i-1个格子取红色,则最后两个格子所放置的骨牌对前面格子的2^(i-2)种涂法各有1贡献,取dp[i-2] + 2^(i-2)。故dp[i] = dp[i-1] + 2*dp[i-2] + 2^(i-2)

实现代码

  • dp可预处理。
  • trans:实现矩阵转置。
  • calc:算横条的总贡献。
#include 
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 3e5 + 5;
const int mod = 998244353;

int n, m;
vector<string> a;
LL p2[N], dp[N];

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
    read (x);
    read (r...);
}

vector<string> trans (vector<string> &a) {
    if (!a.size() ) return vector<string>();
    vector<string> b (a[0].size() );
    re_ (i, 0, a[0].size() ) {
        re_ (j, 0, a.size() ) b[i] += a[j][i];
    }
    swap (n, m);
    return b;
}

LL calc (vector<string> &a, int tot_o) {
    LL ans = 0;
    re_ (i, 0, n) {
        int cnt = 0;
        re_ (j, 0, m) {
            if (a[i][j] == 'o') ++cnt;
            if (a[i][j] != 'o' || j + 1 == m) {
                ans = (ans + dp[cnt] * p2[tot_o - cnt] % mod) % mod;
                cnt = 0;
            }
        }
    }
    return ans;
}

int main() {
    read (n, m);
    re_ (i, 0, n) {
        string t;
        cin >> t;
        a.push_back (t);
    }
    p2[0] = 1, p2[1] = 2;
    rep (i, 2, N - 5) {
        p2[i] = 2 * p2[i - 1] % mod;
        dp[i] = (dp[i - 1] + 2 * dp[i - 2] % mod + p2[i - 2]) % mod;
    }
    int tot_o = 0;
    re_ (i, 0, n) re_ (j, 0, m) tot_o += (a[i][j] == 'o');
    LL ans = calc (a, tot_o);
    a = trans (a);
    ans = (ans + calc (a, tot_o) ) % mod;
    printf ("%lld\n", ans);
    return 0;
}

参考链接

  1. https://www.cnblogs.com/luoyicong/p/14651349.html

你可能感兴趣的:(数论&组合数学,贪心,c++,算法,python,贪心算法,动态规划)