牛客小白月賽105 題解

XYukari發表於2024-11-23

A. lz的吃飯問題

print("lz" if (lambda a,b: a*b)(*map(int, input().split())) < (lambda a,b: a*b)(*map(int, input().split())) else "gzy")

B. lz的數字問題

把數字按字串處理,找到小數點則分成整數和小數兩段;沒有小數點則整個字串都賦給整數部分,小數部分為空串。得到 \(a_1,a_2,b_1,b_2\) 四部分。把 \(a_2,b_2\) 長度補到 6,然後比較是否有 \(a_1=b_1,a_2=b_2\) 即可。

string a, b; cin >> a >> b;
// 處理數字a
int n = a.size(); string a1, a2; // 整數、小數部分
for (int i = 0; i < n; i++)
    if (a[i] == '.')  a1 = a.substr(0, i), a2 = a.substr(i + 1, min(n - i - 1, 6));
if (a1.empty()) a1 = a;
while (a2.size() < 6) a2 += '0';
// 處理數字 b
int m = b.size(); string b1, b2;
for (int i = 0; i < m; i++)
    if (b[i] == '.')  b1 = b.substr(0, i), b2 = b.substr(i + 1, min(m - i - 1, 6));
if (b1.empty()) b1 = b;
while (b2.size() < 6) b2 += '0';
// 輸出答案
cout << ((a1 == b1 && a2 == b2) ? "YES\n" :"NO\n");

C. lz的蛋撻問題

注意到對於一個位於第一行的白色格子,只有 4 種情況下,把它變黑可以增加連通塊數目:

  • 右側、下方是白色,右下角是黑色;
  • 左側、下方是白色,左下角是黑色;
  • 左右兩邊是白色,下方是黑色;
  • 左右下都是黑色。

對於第二行的格子,把“下方“改成上方即可,用二維陣列搭配 i^1 切換上下可以統一。對於邊緣的格子,可以在兩頭各加上一列黑色格子,方便處理邊界情況。

for (int i = 0; i <= 1; i++) {
    scanf("%s", s[i] + 1);
    s[i][0] = s[i][n + 1] = 'x';
}
int ans = 0;
for (int i = 0; i <= 1; i++) {
    for (int j = 1; j <= n; j++) {
        if (s[i][j] == 'x') continue;
        if (s[i][j - 1] == '.' && s[i][j + 1] == '.' && s[i ^ 1][j] == 'x') ans++
        else if (s[i][j + 1] == '.' && s[i ^ 1][j] == '.' && s[i ^ 1][j + 1] == 'x') ans++;
        else if (s[i][j - 1] == '.' && s[i ^ 1][j] == '.' && s[i ^ 1][j - 1] == 'x') ans++;
        else if (s[i][j - 1] == 'x' && s[i][j + 1] == 'x' && s[i ^ 1][j] == 'x') ans++;
    }
}
printf("%d\n", ans);

D. lz的染色問題

對於 \(m\) 個詢問,用並查集維護哪些花顏色要求相同。把要求同種顏色的花取出來,求一個眾數,然後剩下的花顏色改成眾數。可以 \(O(n+m)\),但帶 \(\log\) 也能過。

int count(vector<int>& v) { // 求眾數個數
    sort(v.begin(), v.end(), [&](int i, int j) {  return c[i] < c[j]; }); // 排序,同種顏色的變成連續段
    int mxcnt = 1, cnt = 1;
    for (int i = 1; i < v.size(); i++)
        if (c[v[i]] == c[v[i - 1]]) cnt += 1;
        else mxcnt = max(mxcnt, cnt), cnt = 1;
    return max(mxcnt, cnt); // 注意處理最後一段
}
int main() {
    int n, m, ans = 0; cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> c[i], pa[i] = i;
    while (m--) {
        int x, y; cin >> x >> y;
        int px = find(x), py = find(y);
        if (px != py)  pa[px] = py;
    }
    map<int, vector<int>> mp;
    for (int i = 1; i <= n; i++) mp[find(i)].push_back(i); // 要求同種顏色的放在一起
    for (auto& [i, v] : mp)  ans += v.size() - count(v); // 都變成眾數最優
    cout << ans << '\n';
}

E. lz的括號問題

一個括號序列可以分成幾個合法的括號序列,比如 ((()))(()) 可以分成 ((()))(())。自己所在的序列之外的其它括號,顯然都可以在自己前面匹配掉;而自己所在的序列中,這個括號之間的括號都可以在它之前匹配掉。把這兩部分加起來就是該括號的答案。

更簡單的做法是在彈棧之後看看棧裡還有 \(x\) 對括號,這些括號沒法在自己之前匹配上,能匹配上的就是 \(n-x\) 對。

for (int i = 1; i <= n * 2; i++)
    if (s[i] == '(') st.push(i);
    else if (st.empty()) return puts("-1"), 0;
    else cnt[st.top()] = st.size(), st.pop();
if (!st.empty()) return puts("-1"), 0;
for (int i = 1; i <= n * 2; i++)
    if (s[i] != ')') printf("%d ", n - cnt[i]);

F. lz的序列問題

線段樹,考慮如何合併這個字首積和 \(sum\),發現可以左區間 \(sum\) + 右區間 \(sum \times\) 左區間區間積,而這兩個屬性都是支援區間賦值的——區間積直接快速冪,\(sum\) 可以用等比數列求和做。注意取模即可。

using Mod = ModInt<LL, 1000000007>; // 用了模數類
const int N = 1e5 + 10;
int n, q, a[N];
struct segtree {
    int l, r;
    Mod sum, prod, tag;
} t[N << 2];
#define ls p << 1
#define rs p << 1 | 1
#define mid ((t[p].l + t[p].r) >> 1)

inline Mod power(Mod a, int n); // 快速冪(略)
inline void refresh(int p) {
    t[p].prod = t[ls].prod * t[rs].prod;
    t[p].sum = (t[ls].sum + t[ls].prod * t[rs].sum);
}
void build(int p, int l, int r);
inline void pushup(int p, Mod v) {
    t[p].tag = v;
    int len = t[p].r - t[p].l + 1;
    t[p].prod = power(v, len);
    t[p].sum = (v == 1) ? len : (power(v, len + 1) - v) / (v - 1); // 注意公比q=1時不能用公式(除0)
}
inline void pushdown(int p) {
    if (t[p].tag == 0) return;
    pushup(ls, t[p].tag);
    pushup(rs, t[p].tag);
    t[p].tag = 0;
}
void fill(int p, int l, int r, Mod v) {
    if (l <= t[p].l && t[p].r <= r) return pushup(p, v), void(0);
    pushdown(p);
    if (l <= mid) fill(ls, l, r, v);
    if (r > mid) fill(rs, l, r, v);
    refresh(p);
}
Mod get_prod(int p, int l, int r) {
    if (l <= t[p].l && t[p].r <= r) return t[p].prod;
    pushdown(p);
    Mod res = 1;
    if (l <= mid) res = res * get_prod(ls, l, r);
    if (r > mid) res = res * get_prod(rs, l, r);
    return res;
}
Mod get_sum(int p, int l, int r) {
    if (l <= t[p].l && t[p].r <= r) return t[p].sum;
    pushdown(p);
    if (l > mid) return get_sum(rs, l, r); // 注意不要寫反ls,rs
    if (r <= mid) return get_sum(ls, l, r);
    return (get_sum(ls, l, r) + get_prod(ls, l, r) * get_sum(rs, l, r));
}
int main() {
    cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> a[i];
    build(1, 1, n);
    while (q--) {
        int op, l, r; Mod x;
        cin >> op;
        if (op == 1) {
            cin >> l >> r >> x;
            fill(1, l, r, x);
        } else if (op == 2) {
            cin >> l >> r;
            cout << get_sum(1, l, r) << endl;
        }
    }
}