VP Educational Codeforces Round 25


A. Binary Protocol

题意:一个数字的数位上的数变成了其值为长度的连续个1,并用0把这些串连接在一起。现在要你恢复原来的数字。

用个\(sum\)记录连续的1的个数,每次遇到0就把\(sum\)加入答案,并使\(sum=0\)

点击查看代码
void solve() {
	int n;
	std::cin >> n;
    std::string s;
    std::cin >> s;
    std::string ans;
    int cnt = 0;
    for (int i = 0; i < n; ++ i) {
    	if (s[i] == '1') {
    		++ cnt;
    	} else {
    		ans += std::to_string(cnt);
    		cnt = 0;
    	}
    }

   	ans += std::to_string(cnt);


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

B. Five-In-a-Row

题意:两个人下五子棋,判断下一回合能不能赢。

模拟题。每个空位往八个方向枚举就行了。

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

    auto get = [&](int x, int y, int dx, int dy) -> int {
    	int res = 0;
    	x += dx, y += dy;
    	while (x >= 0 && x < n && y >= 0 && y < n && s[x][y] != 'O' && s[x][y] != '.') {
    		++ res;
    		x += dx, y += dy;
    	}

    	return res;
    };

    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		if (s[i][j] == '.') {
    			if (get(i, j, 0, -1) + get(i, j, 0, 1) >= 4 || get(i, j, 1, 0) + get(i, j, -1, 0) >= 4 || 
    				get(i, j, -1, -1) + get(i, j, 1, 1) >= 4 || get(i, j, -1, 1) + get(i, j, 1, -1) >= 4) {
    				std::cout << "YES\n";
    				return;
    			}
    		}
    	}
    }

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

C. Multi-judge Solving

题意:开始有一个\(k\),和一个数组\(a\),你每次可以拿\(a_i \leq 2k\)\(i\),然后你的\(k\)会与拿的数取最大值,你也可以拿\(a\)里没有的数,花费1的代价。求把\(a\)里数都拿完的最小代价。

判断后从小到大拿,同时更新\(k\),遇到拿不到的就一直乘二到可以拿为止。

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

    std::sort(a.begin(), a.end());
    int ans = 0;
    for (int i = 0; i < n; ++ i) {
    	if (a[i] <= 2 * k) {
    		k = std::max(k, a[i]);
    	} else {
    		++ ans;
    		k = 2 * k;
    		-- i;
    	}
    }

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

D. Suitable Replacement

题意:给你两个字符串\(s, t\)\(s\)有些地方没有填,你把这些地方填上后进行重排后使得\(t\)出现的次数最多。

因为可以重排,那么我们想让\(t\)出现的最多只需要让对应字母出现的次数最多就行了。
考虑二分,如果我们需要\(mid\)\(t\),那么每次\(t\)的字符需要出现\(cnt_i \times mid\)次。判断不够的是不是小于等于未填的位置个数。

点击查看代码
void solve() {
    std::string s, t;
    std::cin >> s >> t;
    int n = s.size();
    int k = std::count(s.begin(), s.end(), '?');
    std::vector<int> cntt(26), cnts(26);
    for (auto & c : s) {
    	if (c != '?') {
    		++ cnts[c - 'a'];
    	}
    }

    for (auto & c : t) {
    	++ cntt[c - 'a'];
    }

    auto check = [&](int m) -> bool {
    	int sum = 0;
    	for (int i = 0; i < 26; ++ i) {
	    	sum += std::max(0, cntt[i] * m - cnts[i]);
	    }

	    return sum <= k;
    };

    int l = 0, r = n / (int)t.size(); 
    while (l < r) {
    	int mid = l + r + 1 >> 1;
    	if (check(mid)) {
    		l = mid;
    	} else {
    		r = mid - 1;
    	}
    }

    std::stack<char> stk;
    for (int i = 0; i < 26; ++ i) {
    	int sum = std::max(0, cntt[i] * l - cnts[i]);
    	while (sum -- ) {
    		stk.push(char(i + 'a'));
    	}
    }

    while (stk.size() < k) {
    	stk.push('a');
    }

    for (auto & c : s) {
    	if (c == '?') {
    		c = stk.top();
    		stk.pop();
    	}
    }

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

E. Minimal Labels

题意:一个有向无环图,你要构造一个排列\(p\),使得如果\(u\)\(v\)有边,则\(p_u < p_v\)。且排列的字典序最小。

没有入度的点一定使填最大的数,同时我们需要字典序最小,那么把这些点从大到小填。同时发现填完一个数后就会多出一些入度为0的点,然后可以填的数减一。这时就变成了一个子问题。于是我们用大根堆模拟拓扑排序,先出队的点填大的数。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<int>> adj(n);
    std::vector<int> in(n);
    for (int i = 0; i < m; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[v].push_back(u);
    	++ in[u];
    }

    std::vector<int> ans(n);
    std::priority_queue<int> heap;
    for (int i = 0; i < n; ++ i) {
    	if (!in[i]) {
    		heap.push(i);
    	}
    }

    int num = n;
    while (heap.size()) {
    	int u = heap.top(); heap.pop();
    	ans[u] = num -- ;
    	for (auto & v : adj[u]) {
    		if ( -- in[v] == 0) {
    			heap.push(v);
    		}
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n - 1];
    }
}

F. String Compression

题意:对于一个字符串可以表示为若干个\(cnt_i, s_i\),表示连续出现了\(cnt_i\)\(s_i\),明显一个字符串有多种表现形式,你需要求出长度最小的表示。

对于一个字符串\(s\),对它进行\(kmp\),那么如果\(n \% (n - next[i]) == 0\),则有一个长度为\(n - next[i]\)的循环。那么我们可以枚举\(j\),做\([j, n]\)\(kmp\),然后对于每个\(i\)看有没有循环。\(dp\)求解。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();
    s = " " + s;
    const int inf = 1e9;
    std::vector<int> f(n + 1, inf);
    f[0] = 0;

    std::vector<int> cnt(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	cnt[i] = cnt[i / 10] + 1;
    	f[i] = i + 1;
    }

	std::vector<int> next(n + 1);
    for (int k = 1; k <= n; ++ k) {
    	next[k] = k - 1;
    	for (int i = k + 1, j = k - 1; i <= n; ++ i) {
    		while (j >= k && s[i] != s[j + 1]) {
    			j = next[j];
    		}

    		j += s[i] == s[j + 1];
    		next[i] = j;
    	}

    	for (int i = k; i <= n; ++ i) {
			int len = i - k + 1;
			if (next[i] >= k && len % (len - (next[i] - k + 1)) == 0) {
				f[i] = std::min(f[i], f[k - 1] + cnt[len / (len - (next[i] - k + 1))] + (len - (next[i] - k + 1)));
			}
			f[i] = std::min(f[i], f[k - 1] + 1 + len);
    	}
    }

    std::cout << f[n] << "\n";
}

G. Tree Queries

题意:给你一棵树,开始每个点都是黑色,\(q\)次操作,每次把一个点染成黑色,或者问你一个点到所有黑点的路径中编号最小的点。强制在线。

可以先把第一个黑色当作根,然后一遍\(dfs\)求出每个点到根的路径上编号最小的点。那么当我们染一个点的时候,就相当于拿它到根的最小编号更新答案。

点击查看代码
void solve() {
    int n, q;
    std::cin >> n >> q;
    std::vector<std::vector<int>> adj(n + 1);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    std::vector<int> f(n + 1, n);
   	auto dfs = [&](auto self, int u, int fa) -> void {
   		f[u] = std::min(f[u], u);
   		for (auto & v : adj[u]) {
   			if (v == fa) {
   				continue;
   			}

   			f[v] = f[u];
   			self(self, v, u);
   		}
   	};

    int t, u;
    std::cin >> t >> u;
    u = u % n + 1;
    dfs(dfs, u, 0);
    int ans = u;

    -- q;
    int last = 0;
    while (q -- ) {
    	std::cin >> t >> u;
    	u = (u + last) % n + 1;
    	if (t == 1) {
    		ans = std::min(ans, f[u]);
    	} else {
    		last = std::min(ans, f[u]);
    		std::cout << last << "\n";
    	}
    }
}
posted @ 2025-03-06 18:24  maburb  阅读(27)  评论(0)    收藏  举报