1. 程式人生 > 其它 >Educational Codeforces Round 118 (Div.2)

Educational Codeforces Round 118 (Div.2)

Edu 118(Div.2)

A. Long Comparison

題意

給定四個數字 \(x, p_1, y, p_2\)\(p_1\) 表示 \(x\) 後面的 \(0\) 個數, \(p_2\) 表示 \(y\) 後面的 \(0\) 個數,比較 \(x, y\) 的大小。

分析

  1. 位數相同:根據位數判斷。
  2. 位數不同:把 \(x\)\(y\) 的後導 \(0\) 去掉,再比較 \(x, y\)

Code

/* 終點是一切概率的結束,也是一切期望的開始 */
#include <bits/stdc++.h>
using namespace std;

void solve ()
{
    string a; int p; cin >> a >> p;
    string b; int p2; cin >> b >> p2;
    if (a.size() + p != b.size() + p2)
    {
        if (a.size() + p > b.size() + p2) cout << '>' << endl;
        else cout << '<' << endl;
    }
    else
    {
        while(a[a.size() - 1] == '0') p ++ , a = a.substr(0, a.size() - 1);
        while(b[b.size() - 1] == '0') p2 ++, b = b.substr(0, b.size() - 1);
        if (a == b) cout << '=' << endl;
        else if (a > b) cout << '>' << endl;
        else cout << '<' << endl;
    }
}

signed main ()
{
    cout.tie(0)->sync_with_stdio(0);
    int _; for (cin >> _; _--; ) solve();
    return 0;
}

B. Absent Remainder

題意

給定長度為 \(n\) 的序列 \(a\),找出 \(\lfloor \dfrac n 2 \rfloor\) 個序偶 \((x, y)\) 滿足:

  1. \(x != y\)
  2. \(x \in a, y \in a\)
  3. \(x \ \ mod \ \ y \notin a\)

分析

對序列排序,輸出後面 \(\lfloor \dfrac n 2 \rfloor\)\(a_1\) 組成的序偶即可,因為模數一定小於 \(a_1\) ,而 \(a_1\) 是序列最小的數字,因此模數一定不存在在 \(a\) 中。

Code

/* 終點是一切概率的結束,也是一切期望的開始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
using namespace std;
const int N = 200010;

int a[N];

void solve ()
{
    int n; cin >> n;
    rep(i, 1, n) cin >> a[i];
    sort(a + 1, a + n + 1);
    for (int i = 0, p = 2; i < n / 2; i ++, p ++ )
        cout << a[p] << ' ' << a[1] << endl;
}

signed main ()
{
    cout.tie(0)->sync_with_stdio(0);
    int _; for (cin >> _; _--; ) solve();
    return 0;
}

C. Poisoned Dagger

題意

敵人有 \(h\) 血量,你有 \(n\) 次攻擊,第 \(i\) 次攻擊會從 \(a_i\) 秒開始,每秒對敵人造成 \(1\) 血量,同時前面的攻擊無效(即每秒只會造成 \(1\) 傷害),攻擊有 \(k\) 秒的延長時間,即如果從 \(a_i\) 開始攻擊,敵人會在 \(a_i, a_{i+1} \ldots a_{i+k-1}\) 秒受到傷害。問 \(k\) 的最小值。

分析

每次攻擊會造成 \(min(k, d_i)\) 傷害 (\(d_i\) 為兩次攻擊的間隔)。

由於答案有單調性,可以二分列舉 \(k\) 的值。

Code

/* 終點是一切概率的結束,也是一切期望的開始 */
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200010;

int d[N]; // 間隔,n-1個
int n, h;

bool check (int mid)
{
    int m = 0;
    for (int i = 1; i < n; i ++ )
        m += min(d[i], mid);
    m += mid;
    return m >= h;
}

void solve ()
{
    cin >> n >> h;
    int last; cin >> last;
    for (int i = 1; i < n; i ++ )
    {
        int x; cin >> x;
        d[i] = x - last;
        last = x;
    }
    int l = 1, r = 1e18 + 10;
    while(l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    cout << r << endl;
}

signed main ()
{
    cout.tie(0)->sync_with_stdio(0);
    int _; for (cin >> _; _--; ) solve();
    return 0;
}

D. MEX Sequences

題意

定義序列為 \(MEX-correct\) 當且僅當序列滿足:\(|x_i - Mex(x_1, x_2 \ldots x_i)| \le 1\)

給定長度為 \(n\) 的序列\(a\) ,問序列 \(a\) 有多少子序列為 \(MEX-correct\)

分析

當一個元素為 \(x\) 時,它的 \(mex\) 只能為 \(x-1\) 或者 \(x+1\)

假設序列某一個字首 \(mex\)\(x\) ,那麼這個字首序列的最大值只能為 \(x-1\)\(x+1\)

所以對於某一個元素 \(x\) ,可以對\(MEX-correct\)分為四類:

  1. \(mex = x-1, max = x\)
  2. \(mex = x-1, max = x-2\)
  3. \(mex = x+1, max = x+2\)
  4. \(mex = x + 1, max = x\)

假設 \((x, y)\) 表示 \((mex, max)\)

我們設 \(dp1(i)\) 表示序列性質為 \((i, i+1)\) 的序列數量,\(dp2(i)\) 表示 \((i, i-1)\) 的序列數量。


對於 \((x-1, x)\) 而言,由於當前元素為 \(x\) ,我們可以選擇把元素加或者不加入 \((x-1, x)\) 序列,不會影響 \((x-1, x)\) 序列性質。

同時可以把 \(x\) 加入到 \((x-1, x-2)\) 序列中使得它變為 \((x-1, x)\) 。但是這個對於 \(x = 1\) 時,\((0, -1)\) 一定方案數為 \(0\) ,但是我們可以選擇直接選擇 \(\{1\}\) ,這個也滿足 \((x-1, x)\) ,貢獻為 \(1\)

所以 \(dp2(x-1) = dp2(x-1) \times 2 + dp1(x-1) + (x == 1)\)


對於 \((x-1, x-2)\) ,它是沒有轉移的。


對於 \((x+1, x+2)\) ,由於 \(mex = x+1\) ,所以序列裡一定有 \(x\) ,加入 \(x\) 不會影響序列性質。

所以 \(dp2(x+1) = dp2(x+1) \times 2\)


對於 \((x+1, x)\) ,可以選擇加入 \(x\) 不改變性質,也可以把 \(x\) 加入 \((x, x-1)\) 轉移為 \((x+1, x)\)

注意當 \(x = 0\)\((0, -1)\) 一定為 \(0\) 個。但是可以直接選擇 \(\{0\}\) ,滿足 \((x+1, x)\) ,貢獻為 \(1\)

所以 \(dp1(x+1) = dp1(x+1) \times 2 + dp1(x) + (x == 0)\)


根據 \(dp1, dp2\) 的定義,把所有方案加起來就是總方案。

Code

/* 終點是一切概率的結束,也是一切期望的開始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define int long long
using namespace std;

const int mod = 998244353;

/*
 * dp1(i) 表示mex為i且最大值為i-1的方案數量
 * dp2(i) 表示mex為i且最大值為i+1的方案數量
*/

void solve ()
{
    int n, ret = 0; cin >> n;
    map<int, int> dp1, dp2;
    rep(i, 1, n)
    {
        int x; cin >> x;
        dp2[x-1] = (dp2[x-1] * 2 + dp1[x-1] + (x == 1)) % mod;
        dp2[x+1] = (dp2[x+1] * 2) % mod;
        dp1[x+1] = (dp1[x+1] * 2 + dp1[x] + (x == 0)) % mod;
    }
    for (auto & [k, v] : dp1) ret = (ret + v) % mod;
    for (auto & [k, v] : dp2) ret = (ret + v) % mod;
    cout << ret << endl;
}

signed main ()
{
    cout.tie(0)->sync_with_stdio(0);
    int _; for (cin >> _; _--; ) solve();
    return 0;
}

E. Crazy Robot

題意

給定矩陣圖,上面有 \(L\) 表示實驗室, \(\#\) 表示障礙物,\(.\) 表示空地。

有一個機器人可能在圖上的任意一個空地上,你可以給它任意下達方向指令,它會選擇一個與指令不同的方向且該方向不為障礙物,向這個方向走,否則不會移動。

問有多少可能的空地,使得機器人在該點時,無論機器人如何行動,總能選擇指令使它到達實驗室。

分析

從實驗室出發,如果機器人只有一個方向有空地,則我們可以選擇走這個空地的指令,那麼機器人一定會向實驗室走。

注意最後輸出地圖不要使用 \(endl\) ,否則輸出 \(10^6\) 次會超時。

Code

/* 終點是一切概率的結束,也是一切期望的開始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
using namespace std; using PII = pair<int, int>;
const int N = 1000010;

const int dr[] = {-1, 1, 0, 0}, dc[] = {0, 0, -1, 1};
PII q[N];

void solve ()
{
    int n, m, x, y; cin >> n >> m;
    vector g(n, vector<char>(m));
    rep(i, 0, n-1) rep(j, 0, m-1)
    {
       cin >> g[i][j];
       if (g[i][j] == 'L') x = i, y = j;
    }
    int hh = 0, tt = -1;
    // 把周圍點加進來
    for (int i = 0; i < 4; i ++ )
    {
        int dx = x + dr[i], dy = y + dc[i];
        if (dx < 0 || dx >= n || dy < 0 || dy >= m || g[dx][dy] == '#') continue;
        q[++ tt] = {dx, dy};
    }
    while(hh <= tt)
    {
        PII t = q[hh ++ ];
        int x = t.first, y = t.second, cnt_free = 0;
        for (int i = 0; i < 4; i ++ )
        {
            int dx = x + dr[i], dy = y + dc[i];
            if (dx < 0 || dx >= n || dy < 0 || dy >= m) continue;
            if (g[dx][dy] == '.') cnt_free ++ ;
        }
        if (cnt_free > 1) continue;
        g[x][y] = '+';
        for (int i = 0; i < 4; i ++ )
        {
            int dx = x + dr[i], dy = y + dc[i];
            if (dx < 0 || dx >= n || dy < 0 || dy >= m) continue;
            if (g[dx][dy] == '.') q[++ tt] = {dx, dy};
        }
    }
    rep(i, 0, n-1)
    {
        rep(j, 0, m-1) cout << g[i][j];
        cout << '\n';
    }
}

signed main ()
{
    cout.tie(0)->sync_with_stdio(0);
    int _; for (cin >> _; _--; )
        solve();
    return 0;
}