• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

yumiym765

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

2025/08/28 训练

继续数组相关的练习
rating : 1200 ~ 1600
<------------------>

E. Klee's SUPER DUPER LARGE Array!!!

链接

\(\hspace{20px}\)Klee 有一个长度为 \(n\) 的数组 \(a\) ,数组中依次包含整数 \([k, k+1, ..., k+n-1]\) 。Klee 希望选择一个索引 \(i\) ( \(1 \leq i \leq n\) ),使得 \(x = |a_1 + a_2 + \dots + a_i - a_{i+1} - \dots - a_n|\) 最小。(\(2 \leq n, k \leq 10^9\))
\(\hspace{20px}\)输出 \(x\) 的最小可能值。

这里有两种思路:
第一种:二分查找
假设没有绝对值的情况,那么 \(x\) 一定是先负后正的,有单调性,那么可以得知最值一定是在使得 \(x = 0\) 的位置取得。
我们设 \(sum_1 = \dfrac{(k + k - i + 1) \times i}{2}\),\(sum_2 = \dfrac{(k + i + k + n + 1) \times n - i}{2}\)。那么就是要二分查找使得 \(sum_1 - sum_2 \ge 0\) 的位置的最小值。我们得到最小值后在他的附近寻找答案即可。时间复杂度为 \(O(logn)\)。

点击查看代码
void solve(){
	long long n, k;
	cin >> n >> k;
	long long l = 1, r = n, ans = -1;
	auto calc = [&](long long i) -> long long{
		long long sum1 = (k + k + i - 1) * i / 2;
		long long sum2 = (k + i + k + n - 1) * (n - i) / 2;
		return sum1 - sum2;
	};
	while(l <= r){
		long long mid = (l + r) >> 1;
		if(calc(mid) >= 0){
			ans = mid;
			r = mid - 1;
		}else{
			l = mid + 1;
		}
	}
	cout << min(llabs(calc(ans - 1)), llabs(calc(ans))) << "\n";
}

第二种:数学公式
还是寻找使得 \(|sum_1 - sum_2|\) 最小的位置,代入得 \(sum_1 - sum_2=\frac{1}{2} (2i^2+(4k-2)i-n^2+n-2kn)\),这是一个二次函数,由于 \(n,k \ge 2\),可知这个函数的对称轴小于 \(0\),那么我们就是要求使得 \(f(x) = sum_1 - sum_2 = 0\) 的 \(x\) 值。用求根公式求解即可。 \(\Delta = 16k^2-16k+4+16nk+8n^2-8n,x = \dfrac{2-4k+\sqrt{\Delta}}{4}\),求得一个小数,在他的附近(向上向下取整)求最值即可。
我们可以化简求 \(x\) 的式子,有:\(\Delta = 4k^2-4k+1+4nk+2n^2-2n,x = \dfrac{1-2k+\sqrt{\Delta}}{2}\),时间复杂度为 \(O(1)\)。

点击查看代码
void print(__int128 x) {
    if(x < 0) { putchar('-'); x = -x; }
    if(x > 9) print(x / 10);
    putchar(x % 10 + '0');
}

void solve(){
    long long n, k;
    cin >> n >> k;
    auto f = [&](long long i) -> long long{
        __int128 ki = k, ii = i, nn = n;
        __int128 ans = ki*ii*4 - ki*nn*2 + ii*ii*2 - ii*2 - nn*nn + nn;
        return (long long)llabs(ans / 2);
    };
    __int128 kk = k, nn = n;
    __int128 d2 = kk*kk*16 - kk*16 + 4 + kk*nn*16 + nn*nn*8 - nn*8;
    long double s = (sqrt((long double)d2) - 4.0*k + 2.0) / 4.0;
    long long s1 = ceil(s), s2 = floor(s);
    long long res = min(f(s1), f(s2));
    print(res);
    cout << "\n";
}

但是这么写要用到 __int128,很不方便,当然也可以考虑用 Python。

<------------------>

A. Concatenation of Arrays

链接

\(\hspace{20px}\)给你 \(n\) 个数组 \(a_1\) , \(\ldots\) , \(a_n\) 。每个数组的长度都是 2。因此, \(a_i = [a_{i, 1}, a_{i, 2}]\) 。您需要将这些数组连接成一个长度为 \(2n\) 的数组,使得数组中的逆序数最小。请注意,您不需要计算实际的逆序数。

按照元素和非递减的顺序排序即可。
证明:设左边的两个数为 \(a,b\),右边的为 \(c,d\),那么就是 \(a+b\le c+d\),我们可以知道肯定有一个 \(a\) 或 \(b\) 小于等于 \(\max(c,d)\)。那么可以保证逆序数至少为 \(2\),反转后由于有一个 \(a\) 或 \(b\) 小于等于 \(c\) 和 \(d\),所以逆序数最多为 \(2\),所以反转后逆序数不会增加,反而会减少。证毕。

点击查看代码
void solve(){
	int n;
	cin >> n;
	vector<pair<int, int>> a(n);
	for(int i = 0, x, y;i < n;i++){
		cin >> x >> y;
		a[i].first = x;
		a[i].second = y;
	}
	sort(a.begin(), a.end(), [](auto& a, auto& b){
		return a.first + a.second < b.first + b.second;
	});
	for(auto _ : a){
		cout << _.first << " " << _.second << " ";
	}
	cout << "\n";
}
·

<------------------>

B. Cost of the Array

链接

\(\hspace{20px}\)给你一个长度为 \(n\) 的数组 \(a\) 和一个偶数 \(k\) ( \(2 \le k \le n\) )。你需要将数组 \(a\) 恰好拆分成 \(k\) 个非空子数组,使得数组 \(a\) 中的每个元素都恰好属于一个子数组。

接下来,所有具有偶数索引的子数组(第二、第四、 \(\ldots\) 、第 \(k\) 个)会被串联成一个数组 \(b\) 中。之后, \(0\) 会被加到数组 \(b\) 的末尾。

数组 \(b\) 的代价定义为 \(b_i \neq i\) 的最小索引 \(i\) 。例如,数组 \(b = [1, 2, 4, 5, 0]\) 的代价是 \(3\) ,因为 \(b_1 = 1\) 、 \(b_2 = 2\) 和 \(b_3 \neq 3\) 。求将数组 \(a\) 优化分割为子数组后,数组 \(b\) 的最小成本。

如果 \(n = k\),那么答案固定,直接求解即可。

posted on 2025-08-28 22:12  羊毛corn  阅读(8)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3