VP Educational Codeforces Round 41 (Rated for Div. 2)


A. Tetris

找每一列的最小值。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> cnt(n);
    for (int i = 0; i < m; ++ i) {
    	int x;
    	std::cin >> x;
    	++ cnt[x - 1];
    }

    int ans = m;
    for (int i = 0; i < n; ++ i) {
    	ans = std::min(ans, cnt[i]);
    }

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

B. Lecture Sleep

前缀和加枚举。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(n), b(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

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

    std::vector<int> sum(n + 1), pre(n + 1), suf(n + 2);
    for (int i = 0; i < n; ++ i) {
    	sum[i + 1] = sum[i] + a[i];
    	pre[i + 1] = pre[i];
    	if (b[i]) {
    		pre[i + 1] += a[i];
    	}
    }

    for (int i = n - 1; i >= 0; -- i) {
    	suf[i + 1] = suf[i + 2];
    	if (b[i]) {
    		suf[i + 1] += a[i];
    	}
    }

    int ans = 0;
    for (int i = 1; i + k - 1 <= n; ++ i) {
    	ans = std::max(ans, pre[i - 1] + suf[i + k] + sum[i + k - 1] - sum[i - 1]);
    }

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

C. Chessboard

题意:有\(4\)\(n\times n\)\(01\)矩阵,要求修改距离少的数,使得按照某种顺序把它们拼成\(2n\times 2n\)的矩阵后相邻两个数没有相同的。

枚举所有组合情况,然后每个情况找到需要更改的最小值。因为任意两个相邻的都不相等,所以矩阵只有两种形态,比较这两个形态就可以知道需要更改的最小值。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector a(4, std::vector<std::string>(n));
    for (int i = 0; i < 4; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		std::cin >> a[i][j];
    	}
    }

    std::vector<int> p(4);
    std::ranges::iota(p, 0);

    auto get = [&]() -> int {
    	std::vector<std::string> s(2 * n);
    	for (int i = 0; i < n; ++ i) {
    		s[i] = a[p[0]][i] + a[p[1]][i];
    	}

    	for (int i = n; i < 2 * n; ++ i) {
    		s[i] = a[p[2]][i - n] + a[p[3]][i - n];
    	}

    	int sum1 = 0, sum2 = 0;
    	for (int i = 0; i < 2 * n; ++ i) {
    		for (int j = 0; j < 2 * n; ++ j) {
    			sum1 += s[i][j] - '0' != (i + j & 1);
    			sum2 += s[i][j] - '0' == (i + j & 1);
    		}
    	}

    	return std::min(sum1, sum2);
    };

    int ans = 2 * n * 2 * n;
    do {
    	ans = std::min(ans, get());
    } while (std::ranges::next_permutation(p).found);

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

D. Pair Of Lines

题意:给你\(n\)个点,判断有没有两条直线使得任意一个点都至少在一条直线上。

我们随便选两个点,它们不在一条直线的概率最小是\(\frac{1}{2}\)。那么我们可以随机一百次,每次选两个点把它们构成的直线上的点都去掉,然后看剩下的点是不是在一条直线上。

点击查看代码
std::mt19937 gen(std::random_device{}());

int rand(int l, int r) {
	std::uniform_int_distribution<int> dis(l, r);
	return dis(gen);
}

void solve() {
    int n;
    std::cin >> n;
    std::vector<std::pair<i64, i64>> a(n);
    for (auto & [x, y] : a) {
    	std::cin >> x >> y;
    }

    auto check = [&](int p1, int p2) -> bool {
    	std::vector<std::pair<i64, i64>> b;
    	for (int i = 0; i < n; ++ i) {
    		if (i == p1) {
    			continue;
    		}

	    	auto & [x1, y1] = a[p1];
	    	auto & [x2, y2] = a[p2];
    		auto & [x3, y3] = a[i];
    		//(x1 - x2) / (y1 - y2) == (x1 - x3) / (y1 - y3)
    		if ((x1 - x2) * (y1 - y3) != (x1 - x3) * (y1 - y2)) {
    			b.emplace_back(a[i]);
    		}
    	}

    	for (int i = 1; i + 1 < b.size(); ++ i) {
    		auto & [x1, y1] = b[i - 1];
    		auto & [x2, y2] = b[i];
    		auto & [x3, y3] = b[i + 1];
    		if ((x1 - x2) * (y1 - y3) != (x1 - x3) * (y1 - y2)) {
    			return false;
    		}
    	}

    	return true;
    };

    int t = 100;
    while (t -- ) {
    	int i = rand(0, n);
    	int j = rand(0, n);
    	while (i == j) {
    		j = rand(0, n);
    	}

    	if (check(i, j)) {
    		std::cout << "YES\n";
    		return;
    	}
    }

    std::cout << "NO\n";
}

E. Tufurama

题意:给你一个数组,求有多少\(i, j\),满足\(i < j, a_i \geq j, a_j \geq i\)

对于每个\(i\),我们考虑它和前面的位置有多少合法的。那么前面位置最多到\(\min(i - 1, a_i)\),判断这个前缀大于等于\(j\)的值有多少。
我们可以离散化加树状数组做。

点击查看代码
template <class T>
struct Fenwick {
    int n;
    std::vector<T> tr;

    Fenwick(int _n) {
        init(_n);
    }

    void init(int _n) {
        n = _n;
        tr.assign(_n + 1, T{});
    }

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

    T query(int x) {
        T res{};
        for (int i = x; i; i -= i & -i) {
            res = res + tr[i];
        }
        return res;
    }

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

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

    auto b = a;
    for (int i = 1; i <= n; ++ i) {
    	b.push_back(i);
    }

    std::sort(b.begin(), b.end());
    b.erase(std::unique(b.begin(), b.end()), b.end());

    auto get = [&](int x) -> int {
    	return std::ranges::lower_bound(b, x) - b.begin() + 1;
    };

    for (auto & x : a) {
    	x = get(x);
    }

    int m = b.size();
    std::vector<std::vector<int>> Q(n + 1);
    for (int i = 2; i <= n; ++ i) {
    	int p = std::min(get(i - 1), a[i - 1]);
    	Q[p].push_back(get(i));
    }

    i64 ans = 0;
    Fenwick<int> tr(m + 1);
    for (int i = 1; i <= n; ++ i) {
    	tr.add(a[i - 1], 1);
    	for (auto & p : Q[i]) {
    		ans += tr.sum(p, m);
    	}
    }

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

F. k-substrings

题意:给你一个字符串,判断每个\(k \in [1, \lceil \frac{n}{2} \rceil]\)\(s[k .. n - k + 1]\)这个子串最长的为奇数的\(border\)的长度。

我们从大到小操作,发现每次只会新加两个字符,这意味着\(ans_i \leq ans_{i+1} + 2\)。那么我们用\(hash\)暴力判断就行,因为\(border\)长度最多增加\(\lceil \frac{n}{2} \rceil\)次,那么我们也最多判断\(\lceil \frac{n}{2} \rceil\)次。
这题卡单哈希,需要用双模哈希。

点击查看代码
const int mod1 = 1000000271, mod2 = 1000000447;

void solve() {
	int n;
	std::cin >> n;
	std::string s;
	std::cin >> s;
	std::vector<int> h1(n + 1), h2(n + 1), p1(n + 1), p2(n + 1);
	for (int i = 0; i < n; ++ i) {
		h1[i + 1] = (h1[i] * 131ll + s[i] - 'a' + 1) % mod1;
	}

	p1[0] = 1;
	for (int i = 1; i <= n; ++ i) {
		p1[i] = p1[i - 1] * 131ll % mod1;
	}

	for (int i = 0; i < n; ++ i) {
		h2[i + 1] = (h2[i] * 131ll + s[i] - 'a' + 1) % mod2;
	}

	p2[0] = 1;
	for (int i = 1; i <= n; ++ i) {
		p2[i] = p2[i - 1] * 131ll % mod2;
	}

	auto get1 = [&](int l, int r) -> int {
		return (h1[r] - ((i64)h1[l - 1] * p1[r - l + 1]) % mod1 + mod1) % mod1;
	};

	auto get2 = [&](int l, int r) -> int {
		return (h2[r] - ((i64)h2[l - 1] * p2[r - l + 1]) % mod2 + mod2) % mod2;
	};

	auto same = [&](int l1, int r1, int l2, int r2) -> bool {
		return get1(l1, r1) == get1(l2, r2) && get2(l1, r1) == get2(l2, r2);
	};	

	std::vector<int> ans((n + 1) / 2 + 1);
	for (int i = (n + 1) / 2, len = -1; i >= 1; -- i) {
		len += 2;
		if (len >= n - i + 1 - i + 1) {
			len -= 2;
		}

		while (len >= 1 && !same(i, i + len - 1, n - i + 1 - len + 1, n - i + 1)) {
			len -= 2;
		}

		ans[i] = len;
	}

	for (int i = 1; i <= (n + 1) / 2; ++ i) {
		std::cout << ans[i] << " \n"[i == (n + 1) / 2];
	}
}
posted @ 2025-03-25 04:57  maburb  阅读(17)  评论(0)    收藏  举报