牛客周赛 Round 90


A. 真爱粉Tk(一)

点击查看代码
void solve() {
    i64 n;
    std::cin >> n;
    if (n * 10 % 25 == 0) {
    	std::cout << "Yes\n";
    } else {
    	std::cout << "No\n";
    }
}


B. 真爱粉Tk(二)

题意:一个只包含\(2, 5\)的字符串,你每次交换两个位置的数,使得最终没有\(25\)这个子序列。求最小操作数。

实际是让\(5\)都在左边,\(2\)都在右边。
那么两个指针分别从前面和后面枚举,找前面的\(2\)和后面的\(5\)交换。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    int l = 0, r = n - 1;
    int ans = 0;
    while (l < r) {
    	while (l < r && s[l] == '5') {
    		++ l;
    	}

    	while (r > l && s[r] == '2') {
    		-- r;
    	}

    	if (l < r) {
    		++ ans;
    		++ l, -- r;
    	}
    }

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

C. Tk的构造数组

题意:给你\(a, b\),重新排列\(b\),使得\(\sum_{i=1}^{n} a_i \times b_i \times i\)最大。

\(a_i\)看作\(a_i \times i\),那么问题变成使得\(\sum_{i=1}^{n} a_i \times b_i\)最大。
问你让最大的数和最大的匹配,次大的和次大的匹配..依次从大到小排就行。

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

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

    std::ranges::sort(a, std::greater<>());
    std::ranges::sort(b, std::greater<>());

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

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

D. 真爱粉Tk(三)

题意:把\(a\)分成\(k\)份,使得每一份的数字变成字符串连接后\(25\)这个序列出现次数最大的最小。

最大最小,考虑二分。
先把每个\(a_i\)转成字符串。那么假设选择一个部分最多\(mid\)\(25\)子序列,则可以每次从左端点开始向右移,直到\(25\)出现次数大于\(mid\),就可以求出来这一段。那么最终分出的段数小于等于\(k\)就可行。
\(25\)作为子序列出现的个数,就是从前往后记录\(2\)的值,每次遇到\(5\)就累加\(2\)的个数。

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

    auto check = [&](i64 x) -> bool {
    	int cnt = 0;
    	for (int i = 0; i < n; ++ i) {
    		i64 pre = 0, sum = 0;
    		int j = i;
    		while (j < n && sum <= x) {
    			for (auto & c : a[j]) {
    				if (c == '2') {
    					++ pre;
    				} else if (c == '5') {
    					sum += pre;
    				}
    			}

    			if (sum > x) {
    				break;
    			}

    			++ j;
    		}

    		if (i == j) {
    			return false;
    		}

    		i = j - 1;
    		++ cnt;
    	}

    	return cnt <= k;
    };

    i64 l = 0, r = 1e18;
    while (l < r) {
    	i64 mid = l + r >> 1ll;
    	if (check(mid)) {
    		r = mid;
    	} else {
    		l = mid + 1;
    	}
    }

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

E. Tk的染色树

题意:一棵树,每次选择一个点花费\(2\times a_i\)把它染成黑色,或者选择\(i, j\)花费\(a_i + a_j\)把它们的路径上的点都染成黑色。求全部点染成黑色的最小花费。

一开始以为是树形\(dp\),卡了好久。
其实就是考虑叶子怎么选择。如果有偶数个叶子,那么这些叶子两两匹配,就可以覆盖所有点,代价为叶子的和。
如果有奇数个叶子,则同样两两匹配,剩下一个和树里最小的点操作,代价是叶子和加最小值。

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

    if (n == 1) {
    	std::cout << a[0] * 2 << "\n";
    	return;
    }

    std::vector<int> deg(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	++ deg[u]; ++ deg[v];
    }

    i64 sum = 0, cnt = 0, min = 1e18;
    for (int i = 0; i < n; ++ i) {
    	if (deg[i] == 1) {
    		sum += a[i];
    		++ cnt;
    	} 
		min = std::min(a[i], min);
    }

    i64 ans = 1e18;

    if (cnt % 2 == 0) {
    	ans = std::min(ans, sum);
    } else {
    	ans = std::min(ans, sum + min);
    }

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

F. Tk的排列间异或

题意:给你一个长度为\(n\)的排列\(a\),构造一个排列\(b\),使得\(\sum_{i=1}^{n} a_i \oplus b_i\)最大。\(n\)是偶数。

给出的排列并不重要,我们只需要求出\(i\)这个数和哪个数匹配就行。
考虑从大到小枚举\(i\),找和\(i\)异或最大的小于它的数就行。然后标记一些这个数被选过了,那么到了这个数直接跳过。
从大到小枚举不会有两个数选同一个数,而从小到大枚举可以有多个数选同一个数。
怎么找小于\(x\)的异或最大的数?从高位往低位枚举,如果这一位\(x\)\(1\)则不用管,如果是\(0\),则看加上这一位会不会超过\(x\)

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

    std::vector<int> p(n + 1), st(n + 1);
    for (int i = n; i >= 1; -- i) {
    	if (st[i]) {
    		continue;
    	}
    	int x = 0;
    	for (int j = 20; j >= 0; -- j) {
    		if (~i >> j & 1) {
    			if (x + (1 << j) <= i) {
    				x += 1 << j;
    			}
    		}
    	}

    	st[x] = 1;
    	p[i] = x;
    	p[x] = i;
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cout << p[a[i]] << " \n"[i == n];
    }
}
posted @ 2025-04-20 21:28  maburb  阅读(96)  评论(0)    收藏  举报