Educational Codeforces Round 70

Posted dup4

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Educational Codeforces Round 70相关的知识,希望对你有一定的参考价值。

Contest Info


Practice Link

Solved A B C D E F
5/6 ? ? ? ? ? -
  • O 在比赛中通过
  • ? 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. You Are Given Two Binary Strings...

题意:
给出两个数字\(x, y\),令\(f(x)\)\(x\)的二进制表示,现在要选择一个\(k\)使得\(s_k = f(x) + f(y) \cdot 2^k\)的字典序最小。

思路:
考虑乘上\(2^k\)相当于让\(f(y)\)左移\(k\)位,那么我们肯定要让\(f(y)\)的最后一位\(1\)移到离\(f(x)\)最近的一个\(1\)和它进位之后,这样翻转之后字典序是最小的。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define N 100010
char s[N], t[N];
 
int main() 
    int T; scanf("%d", &T);
    while (T--) 
        scanf("%s%s", s + 1, t + 1);
        int lens = strlen(s + 1);
        int lent = strlen(t + 1);
        int post = 0;
        for (int i = lent; i >= 1; --i) 
            if (t[i] == '1') 
                post = lent - i;
                break;
            
        
        int res = 0;
        for (int i = lens - post; i >= 1; --i) 
            if (s[i] == '1') 
                res = lens - post - i;
                break;
            
        
        printf("%d\n", res);
    
    return 0;

B. You Are Given a Decimal String...

题意:
\(x-y\)计数器,它是这样工作的:

  • 初始时数值为\(0\)
  • 给数值加上\(x\)或者\(y\),然后输出数值模\(10\)后的结果
  • 重复第二步操作,直到输出自己满意的序列
    现在给定一段序列,问对于\(x \in [0, 9], y \in [0, 9]\)\(100\)种计数器,最少需要增加多少数才能使得输出的序列是x-y$计数器合法输出来的?

思路:
考虑对于序列中间隔的两个数\(s[i]\)\(s[i + 1]\),我们定义它们之间的差值为\((s[i + 1] - s[i] + 10) \% 10\),因为是数值的最后一位,所以它们的真实差距肯定可以写成\(10k + (s[i + 1] - s[i])\),但是我们并不需要关心\(k\)是多少。
我们只需要知道需要多少个\((px + qy) \% 10 = (s[i + 1] - s[i] + 10) \% 10\)
这个暴力枚举一下\(p, q\)即可,而显然\(p \in [0, 9], q \in [0, 9]\),因为存在模\(10\)操作,\((px + qy) \% 10 = (p \% 10 x + q \% 10 ) \% 10\)

代码:

#include <bits/stdc++.h>
using namespace std;
 
const int INF = 0x3f3f3f3f;
const int N = 2e6 + 10; 
char s[N];
int n, b[20];
void Min(int &x, int y) 
    if (x > y) x = y;

 
int main() 
    while (scanf("%s", s + 1) != EOF) 
        n = strlen(s + 1);
        for (int x = 0; x < 10; ++x) 
            for (int y = 0; y < 10; ++y) 
                for (int k = 0; k < 10; ++k) b[k] = INF;
                for (int p = 0; p < 10; ++p) 
                    for (int q = 0; q < 10; ++q) if (p | q) 
                        Min(b[(p * x + q * y) % 10], p + q - 1);
                    
                
                int ans = 0;
                for (int i = 1; i < n; ++i) 
                    int t = (s[i + 1] - s[i] + 10) % 10;
                    if (b[t] == INF) 
                        ans = -1;
                        break;
                    
                    ans += b[t];
                
                printf("%d%c", ans, " \n"[y == 9]);
            
        
    
    return 0;

C. You Are Given a WASD-string...

题意:
有一个机器人,现在给出一系列行走步骤,问能否在任意处添加一个步骤,使得它的行动范围尽量小。
行动范围的定义为用一个最小的矩形框住它的行动路径。范围即为矩形的面积。

思路:
将横竖分开考虑,如果可以缩小行动范围,那么必然是长缩短一或者宽缩短一。
我们考虑什么时候边长可以缩短一:

  • 考虑

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 200010
char s[N];
 
int main() 
    int T; scanf("%d", &T);
    while (T--) 
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        int nowx = 0, nowy = 0;
        int up = 0, down = 0, left = 0, right = 0;
        int gup = 0, gdown = 0, gleft = 0, gright = 0;
        for (int i = 1; i <= n; ++i) 
            if (s[i] == 'W') --nowx;
            if (s[i] == 'S') ++nowx;
            if (s[i] == 'A') --nowy;
            if (s[i] == 'D') ++nowy;
            up = min(up, nowx);
            down = max(down, nowx);
            left = min(left, nowy);
            right = max(right, nowy);
            gup = max(gup, nowx - up);
            gdown = max(gdown, down - nowx);
            gleft = max(gleft, nowy - left);
            gright = max(gright, right - nowy);
               
        ll x[2], y[2];
        x[0] = max(gup, gdown);
        x[1] = max(1ll * (gup || gdown), x[0] - (gup != gdown));
        y[0] = max(gleft, gright);
        y[1] = max(1ll * (gleft || gright), y[0] - (gleft != gright));
        printf("%lld\n", min((x[0] + 1) * (y[1] + 1), (x[1] + 1) * (y[0] + 1)));
    
    return 0;

D. Print a 1337-string...

题意:
构造一个序列,这个序列只包含\(\1, 3, 7\\),并且一共有\(n\)的子序列是\(1337\)

思路:
考虑最后一位放\(7\)
然后放\(x\)\(3\)
那么每放一个\(1\)可以产生\([1, \fracx(x - 1)2]\)\(1337\)
那么考虑从大到小贪心放即可,总能放完。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 100010
ll n;
ll f(ll x) 
    return x * (x - 1) / 2;

 
int main() 
    int T; scanf("%d", &T);
    while (T--) 
        scanf("%lld", &n);
        for (int i = 32000; i >= 2; --i) 
            while (f(i) <= n) 
                n -= f(i);
                putchar('1');
            
            putchar('3');
        
        assert(n == 0);
        puts("37");
    
    return 0;

E. You Are Given Some Strings...

题意:
给出一个文本串\(T\),和若干个模式串\(S_i\),定义\(f(t, s)\)\(s\)\(t\)中出现的次数。
计算下式:
\[ \begineqnarray* \sum\limits_i = 1^n \sum\limits_j = 1^n f(t, s_i + s_j) \endeqnarray* \]

思路:
考虑\(T\)对整体的贡献,即使\(T\)中存在多少个子串,使得可以从某个中间位置切开,使得左右两边都是模式串。
那么枚举这个中间位置即可,做两边\(AC\)自动机,求出\(f[i], g[i]\),分别表示以\(i\)结尾的模式串匹配次数和以\(i\)开头的模式串匹配次数。
那么乘一乘即是结果。

代码:

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 200010
int n;
string s[N], t, tr;
ll f[N], g[N];
 
#define ALP 26
struct ACAM 
    struct node 
        int nx[ALP], fail;
        int cnt; 
        node() 
            memset(nx, -1, sizeof nx);
            cnt = 0;  
        
    t[N];
    int root, tot; 
    int que[N], ql, qr;
    //节点从1开始
    int newnode() 
        ++tot;
        t[tot] = node();
        return tot;
    
    void init()  
        tot = 0;
        root = newnode();
    
    void insert(string s) 
        int len = s.size(); 
        int now = root;
        for (int i = 0; i < len; ++i) 
            if (t[now].nx[s[i] - 'a'] == -1)
                t[now].nx[s[i] - 'a'] = newnode();
            now = t[now].nx[s[i] - 'a'];
        
        ++t[now].cnt;
    
    void build() 
        ql = 1, qr = 0;
        t[root].fail = root;
        for (int i = 0; i < ALP; ++i) 
            if (t[root].nx[i] == -1) 
                t[root].nx[i] = root;
             else 
                t[t[root].nx[i]].fail = root;
                que[++qr] = t[root].nx[i];
            
        
        while (ql <= qr) 
            int now = que[ql++];
            t[now].cnt += t[t[now].fail].cnt;  
            for (int i = 0; i < ALP; ++i) 
                if (t[now].nx[i] == -1) 
                    t[now].nx[i] = t[t[now].fail].nx[i];
                
                else 
                    t[t[now].nx[i]].fail = t[t[now].fail].nx[i];
                    que[++qr] = t[now].nx[i];
                
            
        
    
    void query(string s, ll *f) 
        int len = s.size();
        int now = root;
        for (int i = 0; i < len; ++i) 
            now = t[now].nx[s[i] - 'a'];
            f[i] = t[now].cnt;
        
    
ac;
 
int main() 
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    while (cin >> t) 
        tr = t; reverse(tr.begin(), tr.end());
        cin >> n;
        ac.init();
        for (int i = 1; i <= n; ++i) cin >> s[i], ac.insert(s[i]); 
        ac.build(); 
        ac.query(t, f); 
        ac.init();
        for (int i = 1; i <= n; ++i) 
            reverse(s[i].begin(), s[i].end());
            ac.insert(s[i]);
        
        ac.build();
        ac.query(tr, g);
        ll res = 0;
        int len = t.size(); 
        reverse(g, g + len);
        for (int i = 0; i < len - 1; ++i) 
            res += f[i] * g[i + 1];
        
        cout << res << "\n";
    
    return 0;

以上是关于Educational Codeforces Round 70的主要内容,如果未能解决你的问题,请参考以下文章

Educational Codeforces Round 7 A

Educational Codeforces Round 7

Educational Codeforces Round 90

Educational Codeforces Round 33

Codeforces Educational Codeforces Round 54 题解

Educational Codeforces Round 27