牛客周赛 Round 86


A. 小苯跑外卖

点击查看代码
void solve() {
	int x, y;
	std::cin >> x >> y;
	std::cout << (y + x - 1) / x << "\n";
}

B. 小苯的区间删除

任意次操作,把负数都删掉就行。

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

    i64 ans = 0;
    for (int i = 0; i < n; ++ i) {
    	ans += a[i] > 0 ? a[i] : 0;
    }

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

C. 小苯的数字消除

题意:给你一个\(01\)串,你每次可以选择两个相邻且相等的数同时删去。你可以更改任意位置,求使得最后串剩下的数小于等于1个的最小更改数。

先用栈模拟一遍,类似括号匹配,每次遇到相同的消去。
最后剩下的是一个01相间的串,形如\(010101..\)这种。这个串需要\(\frac{len}{2}\)次操作。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
	std::stack<char> stk;
    for (int i = 0; i < n; ++ i) {
    	if (stk.size() && stk.top() == s[i]) {
    		stk.pop();
    	} else {
    		stk.push(s[i]);
    	}
    }

    int ans = (int)stk.size() / 2;
    std::cout << ans << "\n";
}

D. 小苯的数字集合

题意:有一个可重集合。一开始有两个数\(x, y\)。你每次可以选择集合的两个数,然后把它们的与值或者或值或者异或值或者\(gcd\)插入集合。求使得集合出现\(0\)的最少操作数。

首先我们最多需要三次操作,因为\(a \oplus b = c, c \oplus b = a, a \oplus a = 0\)
那么我们分类讨论,一次操作的情况只有与操作和异或操作可以实现。
然后是两次操作的情况,我们把每个操作的值都插入,然后枚举它和\(x, y\)进行的操作看能不能得到\(0\)
否则答案就是3。

点击查看代码
void solve() {
    int x, y;
    std::cin >> x >> y;
    //x^y=z
    //z^y=x
    //x^x=0
    if ((x & y) == 0 || (x ^ y) == 0) {
    	std::cout << 1 << "\n";
    } else {
    	std::vector<int> a{x & y, x | y, x ^ y, std::gcd(x, y)};
    	for (auto & z : a) {
    		if ((x & z) == 0 || (x | z) == 0 || (x ^ z) == 0 || (y & z) == 0 || (y | z) == 0 || (y ^ z) == 0) {
    			std::cout << 2 << "\n";
    			return;
    		}
    	}
    	std::cout << 3 << "\n";
    }
}

E. 小苯的Polygon

题意:给你\(n\)个木棍,每个木棍有长度,你要用他们构造一个凸多边形,使得这个凸多边形的周长最小。

凸多边形的任意一条边长度都比其它\(n-1\)条边的长度之和小。
那么我们枚举最长的一条边,然后把比它小的边进行背包\(dp\)求出可以凑出的长度,取最接近它的。

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

    if (n < 3) {
    	std::cout << -1 << "\n";
    	return;
    } else if (n == 3) {
    	if (a[0] + a[1] <= a[2] || a[0] + a[2] <= a[1] || a[1] + a[2] <= a[0]) {
    		std::cout << -1 << "\n";
    	} else {
    		std::cout << a[0] + a[1] + a[2] << "\n";
    	}
    } else {
    	std::ranges::sort(a);
    	int ans = -1;

    	std::bitset<10010> b;
    	b[0] = 1;
    	for (int i = 0; i < n; ++ i) {
    		if (i >= 2) {
    			for (int j = a[i] + 1; j <= 10000; ++ j) {
    				if (b[j]) {
    					if (ans == -1 || a[i] + j < ans) {
    						ans = a[i] + j;
    					}
    					break;
    				}
    			}
    		}

    		b |= b << a[i];
    	}	
    	std::cout << ans << "\n";
    }
}

F. 小苯的线性dp

题意:给你一个数组,每次你可以进行一次合并操作,把两个数更换为它们的和。求分别在\([0, n - 1]\)操作后数组的最大极差。

首先我们想,合并的这些区间应该尽可能是一个区间,因为我们要让最大值最大,所以应该把数都合并到一起,不过也会出现操作两个区间的情况,这个下面会说。
我们可以用\(st\)表维护区间最大最小值,枚举操作的区间,那么假设这个区间的长度为\(len\),则\(ans[len - 1] = \min(ans[len - 1], \max(sum[i..j], left_{max}, right_{max}) - \min(left_{min}, right_{min})\)
然后我们考虑操作了两个区间的情况,这种情况就是最小值和其它值相差太大,导致我们不能合并这个最小值,于是只能跨过它,进行一些无效操作。那么这意味着\(ans[i] = \max(ans[0], ans[1], .., ans[i - 1], ans[i])\)。但注意最后如果合并到只剩三个数,就是中间是最小值的情况,我们还要进行操作就只能合并它,所以\(ans[n - 2]\)不能和前面的取最大值。
不过\(ac\)后面发现其实我们求的是前后缀的最大最小值,似乎不需要\(st\)

点击查看代码
const i64 inf = 1e18;

template <class Info> 
struct ST {
	std::vector<std::vector<Info>> st;
	ST(std::vector<Info> a) {
		int n = a.size(), m = std::__lg(n) + 1;
		st.assign(n, std::vector<Info>(m));
		for (int i = 0; i < n; ++ i) {
			st[i][0] = a[i];
		}

		for (int j = 1; j < m; ++ j) {
			for (int i = 0; i + (1 << j - 1) < n; ++ i) {
				st[i][j] = st[i][j - 1] + st[i + (1 << j - 1)][j - 1];
			}
		}
	}

	Info query(int l, int r) {
		if (l > r) {
			return {-inf, inf};
		}
		int lg = std::__lg(r - l + 1);
		return st[l][lg] + st[r - (1 << lg) + 1][lg];
	}
};

struct Info {
	i64 max, min;
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	res.max = std::max(a.max, b.max);
	res.min = std::min(a.min, b.min);
	return res;
}

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

    ST<Info> st(info);
    std::vector<i64> sum(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	sum[i] = sum[i - 1] + a[i - 1];
    }

    std::vector<i64> ans(n);
    ans[0] = st.query(1, n).max - st.query(1, n).min;
    for (int i = 1; i <= n; ++ i) {
    	for (int j = i + 1; j <= n; ++ j) {
    		auto l = st.query(1, i - 1);
    		auto r = st.query(j + 1, n);
    		ans[j - i] = std::max(ans[j - i], std::max({sum[j] - sum[i - 1], l.max, r.max}) - std::min(l.min, r.min));
    	}
    }

    for (int i = 1; i + 2 < n; ++ i) {
    	ans[i] = std::max(ans[i], ans[i - 1]);
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n - 1];
    }
}
posted @ 2025-03-23 21:01  maburb  阅读(98)  评论(0)    收藏  举报