牛客小白月赛105 题解

比赛链接

A. lz的吃饭问题

题目大意

给出四个整数 a ,   b ,   c ,   d a, \ b, \ c,\ d a, b, c, d,若 a ⋅ b < c ⋅ d a \cdot b < c \cdot d ab<cd 则输出 l z lz lz,否则输出 g z y gzy gzy

Solution

按照题意模拟即可。

C++ Code

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int a, b;
    std::cin >> a >> b;

    int c, d;
    std::cin >> c >> d;

    if (a * b < c * d) {
        std::cout << "lz\n";
    } else {
        std::cout << "gzy\n";
    }
    
    return 0;
}

B. lz的数字问题

题目大意

给出两个浮点数 a ,   b a, \ b a, b,判断它们是否相等。

  • 相等的意思是 a a a b b b 的整数部分相同,且小数点后六位相同。

数据范围

  • 1 ⩽ ∣ a ∣ ,   ∣ b ∣ ⩽ 2 × 1 0 5 1 \leqslant |a|, \ |b| \leqslant 2 \times 10^5 1a, b2×105

Solution

用字符串读入浮点数。以 a a a 为例,若 a a a 小数部分超过 6 6 6 位,删掉后面的数位,否则加到 6 6 6 位。

C++ Code

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::string a, b;
    std::cin >> a >> b;

    auto work = [&](auto &s) {
        int p = s.find('.');
        if (p != -1) {
            while (s.size() - p - 1 < 6) {
                s += '0';
            }
            while (s.size() - p - 1 > 6) {
                s.pop_back();
            }
        } else {
            s += '.' + std::string(6, '0');
        }
        return s;
    };

    std::cout << (work(a) == work(b) ? "YES\n": "NO\n");
    
    return 0;
}

C. lz的蛋挞问题

题目大意

给定一个 2 × n 2 \times n 2×n 01 01 01 矩阵,其中挨着的若干个 0 0 0 会形成连通块,设当前有 c n t cnt cnt 个连通块。现在要求你选一个位置 ( i ,   j ) (i, \ j) (i, j) 1 ⩽ i ⩽ 2 ,   1 ⩽ j ⩽ n 1 \leqslant i \leqslant 2, \ 1 \leqslant j \leqslant n 1i2, 1jn),满足 g [ i ] [ j ] = 0 g[i][j] = 0 g[i][j]=0,将其变为 g [ i ] [ j ] = 1 g[i][j] = 1 g[i][j]=1 后,使得连通块数量变为 c n t − 1 cnt -1 cnt1。问这样的位置 ( i ,   j ) (i, \ j) (i, j) 有多少个。

Solution

我们把要从 0 0 0 1 1 1 的位置成为中心点。

考虑下面三种形状:

  • 左上角的 0 0 0 为中心点 0 0 0 1 \begin{matrix} 0 & 0 \\ 0 & 1 \end{matrix} 0001
  • 右上角的 0 0 0 为中心点 0 0 1 0 \begin{matrix} 0 & 0 \\ 1 & 0 \end{matrix} 0100
  • 第一排中心的 0 0 0 为中心点 0 0 0 1 \begin{matrix} 0 & 0 & 0 \\ & 1 & \end{matrix} 0010

可以发现,这三种情况下,如果把中心点变成 1 1 1,就能阻断原先的连通块。

除此之外,还有一种特殊情况就是单个 0 0 0 成为连通块,即四面都是 1 1 1 的情况,也能减少连通块。

C++ Code

#include <bits/stdc++.h>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::array<std::vector<int>, 2> g{};
    for (int i = 0; i < 2; i++) {
        g[i].assign(n, 0);
        for (int j = 0; j < n; j++) {
            char c;
            std::cin >> c;
            if (c == 'x') {
                g[i][j] = 1;
            }
        }
    }

    if (n == 1) {
        std::cout << (g[0][0] or g[1][0]) << "\n";
        return 0;
    }

    auto calc = [&](int r) {
        int res = 0;
        for (int i = 0; i < n; i++) {
            if (g[r][i]) {
                continue;
            }
            bool ok = false;
            if (i > 0) {
                if (!g[r][i - 1] and !g[r ^ 1][i] and g[r ^ 1][i - 1]) {
                    ok = true;
                }
            }
            if (i + 1 < n) {
                if (!g[r][i + 1] and !g[r ^ 1][i] and g[r ^ 1][i + 1]) {
                    ok = true;
                }
            }
            if (i > 0 and i + 1 < n) {
                if (!g[r][i - 1] and !g[r][i + 1] and g[r ^ 1][i]) {
                    ok = true;
                }
            }
            if ((i == 0 or g[r][i - 1]) and (i == n - 1 or g[r][i + 1]) and g[r ^ 1][i]) {
                ok = true;
            }
            res += ok;
        }
        return res;
    };

    int ans = calc(0) + calc(1);

    std::cout << ans << "\n";
    
    return 0;
}

D. lz的染色问题

题目大意

给定一个长度为 n n n 的数组 c c c m m m 个下标对 ( p i ,   p j ) (p_i, \ p_j) (pi, pj),满足 1 ⩽ p i ,   p j ⩽ n 1 \leqslant p_i, \ p_j \leqslant n 1pi, pjn
一次操作可以改变一个指定的 c i ( 1 ⩽ i ⩽ n ) c_i(1 \leqslant i \leqslant n) ci(1in),求至少要多少次操作,使得这 m m m ( p i ,   p j ) (p_i, \ p_j) (pi, pj) 都满足 c p i = c p j c_{p_i} = c_{p_j} cpi=cpj

Solution

显然是并查集,直接把 m m m 对下标合并了,然后记录每个连通块里每种颜色的出现次数。设第 i i i 个连通块颜色的出现次数的集合是 C i C_i Ci,共有 k k k 个连通块,则答案为 ∑ i = 1 k ( s u m ( C i ) − max ⁡ ( C i ) ) \sum\limits_{i = 1}^{k} (sum(C_i) - \max(C_i)) i=1k(sum(Ci)max(Ci))

C++ Code

#include <bits/stdc++.h>

struct DSU {
    std::vector<int> f, sz;
    DSU() {}
    DSU(int n) {
        init(n);
    }
    void init(int n) {
        f.resize(n);
        std::iota(f.begin(), f.end(), 0);
        sz.assign(n, 1);
    }
    int find(int x) {
        while (x != f[x]) {
            x = f[x] = f[f[x]];
        }
        return x;
    }
    int size(int x) {
        return sz[find(x)];
    }
    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        sz[x] += sz[y];
        f[y] = x;
        return true;
    }
    bool same(int x, int y) {
        return find(x) == find(y);
    }
};

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, m;
    std::cin >> n >> m;

    std::vector<int> c(n);
    for (int i = 0; i < n; i++) {
        std::cin >> c[i];
    }

    DSU dsu(n);
    for (int i = 0; i < m; i++) {
        int u, v;
        std::cin >> u >> v;
        u--, v--;
        dsu.merge(u, v);
    }

    std::vector<std::map<int, int>> cnt(n);
    for (int i = 0; i < n; i++) {
        cnt[dsu.find(i)][c[i]]++;
    }
    int ans = 0;
    for (int i = 0; i < n; i++) {
        if (dsu.find(i) == i) {
            int max = 0;
            int sum = 0;
            for (const auto &[c, v]: cnt[i]) {
                max = std::max(max, v);
                sum += v;
            }
            ans += sum - max;
        }
    }
    std::cout << ans << "\n";
    
    return 0;
}

E. lz的括号问题

题目大意

给定一个长度为 2 n 2n 2n 的括号序列 s s s,若其 非法,输出 − 1 -1 1

否则,我们对 s s s 按照左括号的出现顺序进行编号,例如 s = ( ( ) ) ( ) s = (())() s=(())() 就会被编号为 122133 122133 122133

对于每个编号 i ∈ [ 1 ,   n ] i \in [1, \ n] i[1, n],求在删除 i i i 之前,至多 还能删掉多少编号。

  • 每次删除只能删除一个连续两个相同编号,且删完后字符串向中心靠拢重排,
  • 例如对于上述 122133 122133 122133,可以删除 22 22 22,删后变为 1133 1133 1133;可以删除 33 33 33,删后变为 1221 1221 1221;但不能删除 11 11 11,因为它不连续,也不能删除 12 12 12,因为它不相同。

下面举例说明: s = ( ( ( ) ) ( ) ( ) ) ( ( ) ) s = ((())()())(()) s=((())()())(()),编号为 12332445516776 12332445516776 12332445516776

对于编号 2 2 2,我 至多 可以先删除 5 5 5 个其他编号,再来删它。一种删除办法是:

  • 删除 33 33 33,变为 122445516776 122445516776 122445516776
  • 删除 44 44 44,变为 1225516776 1225516776 1225516776
  • 删除 55 55 55,变为 12216776 12216776 12216776
  • 删除 77 77 77,变为 122166 122166 122166
  • 删除 66 66 66,变为 1221 1221 1221

Sample

样例输入

5
((()))(())

样例输出

4 3 2 4 3

Solution

首先我们认为对于每个编号 i d x idx idx,除了 i d x idx idx 其他编号全都能在它之前删,也就是答案初始值为 n − 1 n - 1 n1。然后维护一个括号序列的前缀,见到 ( 就进栈,见到 ) 就出栈(如果栈非空)。

  • 当我们遇到 ( 时,设它的编号为 i d x idx idx,此时栈内全都是之前出现过的无法匹配的 (,同时还要被 i d x idx idx 这个编号压着,所以 a n s [ i d x ] = ( n − 1 ) − s t a c k . s i z e ( ) ans[idx] = (n - 1) - stack.size() ans[idx]=(n1)stack.size()
  • 当我们遇到 ) 时,若栈非空且栈顶是 (,直接出栈,否则返回 − 1 -1 1

C++ Code

#include <bits/stdc++.h>

template<class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &a) {
    os << a[0];
    for (int i = 1; i < a.size(); i++) {
        os << " " << a[i];
    }
    return os;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::string s;
    std::cin >> s;
	
    int idx = 0;
    std::string stk;
    std::vector<int> ans(n, n - 1);
    for (auto c: s) {
        if (c == '(') {
            ans[idx++] -= stk.size();
            stk += c;
        } else if (!stk.empty() and stk.back() == '(') {
            stk.pop_back();
        } else {
            std::cout << "-1\n";
            return 0;
        }
    }

    std::cout << ans << "\n";
    
    return 0;
}

F. lz的序列问题

题目大意

给定一个长度为 n n n 的序列 a a a

定义 s ( l ,   r ) = a l + ( a l ⋅ a l + 1 ) + ( a l ⋅ a l + 1 ⋅ a l + 2 ) + ⋯ + ( a l ⋅ a l + 1 ⋯ a r ) s(l, \ r) = a_l + (a_l \cdot a_{l + 1}) + (a_l \cdot a_{l + 1} \cdot a_{l + 2}) + \cdots + (a_l \cdot a_{l + 1} \cdots a_r) s(l, r)=al+(alal+1)+(alal+1al+2)++(alal+1ar)

现在有 q q q 次询问,每次询问如下:

  • 1 l r x:把 a l a_l al a r a_r ar 都修改为 x x x
  • 2 l r:输出 s ( l ,   r ) s(l, \ r) s(l, r),答案对 1000000007 1000000007 1000000007 取模。

Solution

线段树板子题,因为 s ( l ,   r ) s(l, \ r) s(l, r) 可以分治求解。

对每个线段树区间 [ l ,   r ] [l, \ r] [l, r] 维护一个 s ( l ,   r ) s(l, \ r) s(l, r) m ( l ,   r ) m(l, \ r) m(l, r),其中 m ( l ,   r ) = ∏ i = l r a i m(l, \ r) = \prod\limits_{i = l}^{r}{a_i} m(l, r)=i=lrai

[ l ,   r ] [l, \ r] [l, r] 被分为 [ l ,   b ] [l, \ b] [l, b] [ b + 1 ,   r ] [b + 1, \ r] [b+1, r],那么我们有

m ( l ,   r ) = m ( l ,   b ) ⋅ m ( b + 1 ,   r ) s ( l ,   r ) = s ( l ,   b ) + s ( b + 1 ,   r ) ⋅ m ( b + 1 ,   r ) \begin{align*} m(l, \ r) &= m(l, \ b) \cdot m(b + 1, \ r) \\ s(l, \ r) &= s(l, \ b) + s(b + 1, \ r) \cdot m(b + 1, \ r) \end{align*} m(l, r)s(l, r)=m(l, b)m(b+1, r)=s(l, b)+s(b+1, r)m(b+1, r)

对于修改操作,我们记录一个 t a g tag tag,表示要区间推平的数值,则修改操作如下:

  • m ( l ,   r ) = t a g r − l + 1 m(l, \ r) = tag^{r - l + 1} m(l, r)=tagrl+1
  • s ( l ,   r ) = { r − l + 1 , t a g = 1 t a g ⋅ m ( l ,   r ) − 1 t a g − 1 , t a g ≠ 1 s(l, \ r) = \begin{cases} r - l + 1 ,& tag = 1 \\ tag \cdot \frac{m(l, \ r) - 1}{tag - 1}, & tag \neq 1 \end{cases} s(l, r)={rl+1,tagtag1m(l, r)1,tag=1tag=1

C++ Code

#include <bits/stdc++.h>

using i64 = int64_t;

template<class T>
constexpr T power(T a, i64 b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}
template<int P>
struct MInt {
    int x;
    constexpr MInt() : x{} {}
    constexpr MInt(i64 x) : x{norm(x % getMod())} {}
     
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) {
        Mod = Mod_;
    }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const {
        return x;
    }
    explicit constexpr operator int() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1LL * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
};
 
template<>
int MInt<0>::Mod = 998244353;
 
template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();
 
constexpr int P = 1000000007;
using Z = MInt<P>;

template<class Info, class Tag>
struct LSGT {
    #define l(p) (p << 1)
    #define r(p) (p << 1 | 1)
    int n;
    std::vector<Info> info;
    std::vector<Tag> tag;
    LSGT() {}
    LSGT(int _n, Info _v = Info()) {
        init(_n, _v);
    }
    template<class T>
    LSGT(std::vector<T> _init) {
        init(_init);
    }
    void init(int _n, Info _v = Info()) {
        init(std::vector(_n, _v));
    }
    template<class T>
    void init(std::vector<T> _init) {
        n = _init.size();
        info.assign(4 << std::__lg(n), Info());
        tag.assign(4 << std::__lg(n), Tag());
        auto build = [&](auto self, int p, int l, int r) {
            if (r - l == 1) {
                info[p] = _init[l];
                return;
            }
            int m = l + r >> 1;
            self(self, l(p), l, m);
            self(self, r(p), m, r);
            pull(p);
        };
        build(build, 1, 0, n);
    }
    void pull(int p) {
        info[p] = info[l(p)] + info[r(p)];
    }
    void apply(int p, const Tag &v, int len) {
        info[p].apply(v, len);
        tag[p].apply(v);
    }
    void push(int p, int len) {
        apply(l(p), tag[p], len / 2);
        apply(r(p), tag[p], len - len / 2);
        tag[p] = Tag();
    }
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = v;
            return;
        }
        int m = l + r >> 1;
        push(p, r - l);
        if (x < m) {
            modify(l(p), l, m, x, v);
        } else {
            modify(r(p), m, r, x, v);
        }
        pull(p);
    }
    void modify(int p, const Info &v) {
        modify(1, 0, n, p, v);
    }
    Info query(int p, int l, int r, int x, int y) {
        if (l >= y or r <= x) {
            return Info();
        }
        if (l >= x and r <= y) {
            return info[p];
        }
        int m = l + r >> 1;
        push(p, r - l);
        return query(l(p), l, m, x, y) + query(r(p), m, r, x, y);
    }
    Info query(int l, int r) {
        return query(1, 0, n, l, r);
    }
    void Apply(int p, int l, int r, int x, int y, const Tag &v) {
        if (l >= y or r <= x) {
            return;
        }
        if (l >= x and r <= y) {
            apply(p, v, r - l);
            return;
        }
        int m = l + r >> 1;
        push(p, r - l);
        Apply(l(p), l, m, x, y, v);
        Apply(r(p), m, r, x, y, v);
        pull(p);
    }
    void Apply(int l, int r, const Tag &v) {
        return Apply(1, 0, n, l, r, v);
    }
    template<class F>
    int findFirst(int p, int l, int r, int x, int y, F &&pred) {
        if (l >= y or r <= x) {
            return -1;
        }
        if (l >= x and r <= y and !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = l + r >> 1;
        int res = findFirst(l(p), l, m, x, y, pred);
        if (res == -1) {
            res = findFirst(r(p), m, r, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findFirst(int l, int r, F &&pred) {
        return findFirst(1, 0, n, l, r, pred);
    }
    template<class F>
    int findLast(int p, int l, int r, int x, int y, F &&pred) {
        if (l >= y or r <= x) {
            return -1;
        }
        if (l >= x and r <= y and !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = l + r >> 1;
        int res = findLast(r(p), m, r, x, y, pred);
        if (res == -1) {
            res = findLast(l(p), l, m, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findLast(int l, int r, F &&pred) {
        return findLast(1, 0, n, l, r, pred);
    }
    #undef l(p)
    #undef r(p)
};

struct Tag {
    int tag = 0;
    void apply(const Tag &t) {
        if (t.tag != 0) {
            tag = t.tag;
        }
    }
};
struct Info {
    Z sum = 0;
    Z mul = 1;
    void apply(const Tag &t, int len) {
        if (t.tag != 0) {
            mul = power(Z(t.tag), len);
            if (t.tag == 1) {
                sum = len;
            } else {
                sum = t.tag * (mul - 1) / (t.tag - 1);
            }
        }
    }
};
Info operator+(const Info &a, const Info &b) {
    return {a.sum + a.mul * b.sum, a.mul * b.mul};
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::cout << std::fixed << std::setprecision(12);
    
    int n, q;
    std::cin >> n >> q;

    std::vector<int> a(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
    }

    LSGT<Info, Tag> sgt(n);
    for (int i = 0; i < n; i++) {
        sgt.modify(i, {a[i], a[i]});
    }

    while (q--) {
        int o, l, r;
        std::cin >> o >> l >> r;
        l--;

        if (o == 1) {
            int x;
            std::cin >> x;
            sgt.Apply(l, r, {x});
        } else {
            std::cout << sgt.query(l, r).sum << "\n";
        }
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值