Codeforces Round 1051 (Div. 2) 补题记录

Codeforces Round 1051 (Div. 2) 补题记录

D - Inversion Graph Coloring

题目大意:

当一个序列 \(b_1, b_2, \dots, b_k\) 存在一种对每个下标 \(i\) 染上红色或蓝色的方式,使得每一对 \(i < j\)\(b_i > b_j\) 的下标 \(i, j\) 的颜色不同时,我们称这个序列是“好”的。

给定一个序列 \(a_1, a_2, \dots, a_n\),计算它“好”的子序列数量,并对 \(10^9 + 7\) 取模。

思路:

考虑什么样的子序列会被计入答案。注意到,当存在下标 \(i < j < k\)\(a_i > a_j > a_k\) 时,不存在合法的染色方案。因此题目可以转化为求最长递减子序列长度小于等于 \(2\) 的子序列个数。

接着我们考虑对于第 \(i\) 个数,会与前面已经组成的哪些子序列形成新的子序列。

  • 加入最大值 \(j \leq a_i\) 的子序列中。此时由 \(a_i\) 组成的递减子序列长度为 \(1\)

  • 加入最长递减子序列长度为 \(2\) 且末尾数值 \(k \leq a_i < j\) 的子序列中。此时,\(a_i\) 代替 \(k\) 成为最长递减子序列的最后一个元素,长度依旧为 \(2\)

根据上面的两种情况以及不选择第 \(i\) 个元素与前面元素构成子序列的情况,我们进行 \(dp\),状态转移如下:

  • 不选择 \(a_i\)\(dp[i][j][k] \leftarrow dp[i - 1][j][k];\)

  • \(j \leq a_i\)\(dp[i][a_i][k] \leftarrow dp[i - 1][j][k];\)

  • \(k \leq a_i < j\)\(dp[i][j][a_i] \leftarrow dp[i - 1][j][k].\)

时间复杂度 \(O(n^3)\) 足够通过本题的 Easy Version。

接着我们考虑如何优化 \(dp\)

注意到在转移时,存在如下关系:

\[\begin{cases} dp[i][a_i][k] = \sum_{j \leq a_i} dp[i - 1][j][k] \\ \\ dp[i][j][a_i] = \sum_{k \leq a_i} dp[i - 1][j][k] \end{cases} \]

因此可以考虑把第一维砍掉,用树状数组维护 \(dp\) 每行每列的前缀和,求出增量即可。

\(code:\)

#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define i64 long long

const int mod = 1000000007;

template <typename T>
struct Fenwick {
    int n;
    std::vector<T> a;

    Fenwick(int n = 0) {
        init(n);
    }

    void init(int n) {
        this->n = n;
        a.assign(n, T());
    }

    void add(int x, T v) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] += v;
        }
    }

    T sum(int x) {
        auto ans = T();
        for (int i = x; i > 0; i -= i & -i) {
            ans += a[i - 1];
        }
        return ans;
    }

    T rangeSum(int l, int r) {
        return sum(r) - sum(l);
    }
};

void MuBai() {
	int n;
	cin >> n;

	vector<int> a(n + 1);
	for (int i = 1; i <= n; i ++ ) cin >> a[i];

	vector<Fenwick<i64>> row(n + 1), col(n + 1);
	for (int i = 0; i <= n; i ++ ) {
		row[i].init(n + 1);
		col[i].init(n + 1);
	}
	row[0].add(0, 1), col[0].add(0, 1);

	vector f(n + 1, vector<i64>(n + 1, 0));
	for (int i = 1; i <= n; i ++ ) {
		for (int j = 0; j <= n; j ++ ) {
			i64 g = col[j].sum(a[i] + 1) % mod;
			i64 h = (a[i] < j) ? (row[j].sum(a[i] + 1) % mod) : 0;

			if (g) {
				row[a[i]].add(j, g);
				col[j].add(a[i], g);
				f[a[i]][j] += g;
				if (f[a[i]][j] >= mod) f[a[i]][j] -= mod;
			}

			if (h) {
				row[j].add(a[i], h);
				col[a[i]].add(j, h);
				f[j][a[i]] += h;
				if (f[j][a[i]] >= mod) f[j][a[i]] -= mod;
			}
		}
	}

	i64 ans = 0;
	for (int i = 0; i <= n; i ++ ) {
		ans += col[i].sum(n + 1) % mod;
		if (ans >= mod) ans -= mod;
	}

	cout << (ans < mod ? ans : ans - mod) << endl;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);

	int t; cin >> t;
	while (t -- ) MuBai();

	return 0;
}

E - Make Good

题目大意:

给定一个长度为 \(n\) 的括号字符串 \(s\)。可以进行以下操作无数次:

  • 选择一个下标 \(1 \leq i < |s|\),若 \(s[i] = s[i + 1] = '('\),把 \(s[i]\)\(s[i + 1]\) 替换成 \(')'\)

  • 选择一个下标 \(1 \leq i < |s|\),若 \(s[i] = s[i + 1] = ')'\),把 \(s[i]\)\(s[i + 1]\) 替换成 \('('\)

找出一个可以通过上述操作得到的合法括号序列 \(t\),若不存在则输出 \(-1\)

思路:

显然,当 \(n\) 为奇数时,一定无解。

接着在没有操作次数限制的情况下,考虑每次操作后不变的性质。注意到,当我们对 \(i\) 进行操作时,奇数位和偶数位上 \('('\) 的数量同时加一或减一。
\(odd\) 为奇数位上 \('('\) 的个数,\(even\) 为偶数位上 \('('\) 的个数,可以发现 \(diff = odd - even\) 是恒定的。因此只需要满足 \(diff(s) = diff(t)\)\(s\)\(t\) 就是可以互相转换的。

对于一个合法的括号序列 \(ans\),一定有 \(ans[1] = '(', \ ans[n] = ')'\)。设 \(m = \frac{n}{2}\),则奇数位上的 \('('\) 的个数至少为 \(1\),偶数上的 \('('\) 的个数至多为 \(m - 1\),即:\(1 \leq odd \leq m - 1\)。同时,由 \(diff = odd - even = 2 * odd - m\),我们可以推出满足构造答案的条件:

\[2 - m \leq diff \leq m, \ diff \equiv m \ (mod \ 2) \]

接下来,我们考虑用如下骨架来构造答案,可以证明它总是正确的:

\[\underbrace{( \dots (}_{k 个'('} \quad \underbrace{() \dots ()}_{m - k 个 "()"} \quad \underbrace{) \dots )}_{k 个')'} \]

易得,当 \(k\) 为奇数时,\(diff = k - m + 1\),当 \(k\) 为偶数时,\(diff = m - k\)。可以推出:

\[k = \begin{cases} cnt + m - 1, \ cnt < 0 \\ \\ m - cnt, \ cnt \geq 0 \end{cases} \]

code:

#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define i64 long long

void MuBai() {
    int n; string s;
    cin >> n >> s;
    
    if (n & 1) {
        cout << "-1\n";
        return;
    }

    int odd = 0, even = 0, m = n / 2;
    for (int i = 0; i < n; i ++ ) {
        if (s[i] == ')') continue;
        if (i & 1) even++;
        else odd++;
    }

    int cnt = odd - even;
    if (2 - m > cnt || m < cnt || (cnt & 1) != (m & 1)) {
        cout << "-1\n";
        return;
    }

    int k = 0;
    if (cnt >= 0) k = m - cnt;
    else k = cnt + m - 1;

    string ans = "";
    for (int i = 0; i < k; i ++ ) ans += '(';
    for (int i = 0; i < m - k; i ++ ) ans += "()";
    for (int i = 0; i < k; i ++ ) ans += ')';

    cout << ans << endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);

    int t; cin >> t;
    while (t -- ) MuBai();

    return 0;
}
posted @ 2025-11-28 02:26  算法蒟蒻沐小白  阅读(14)  评论(0)    收藏  举报