问题描述
问题陈述
您会得到一个由 n 个整数组成的数组 a 。
您想通过精确执行三遍以下操作来使 a 的所有元素等于零:
选择一个段,对于该段中的每个数字,我们可以为其添加一个 len 的倍数,其中 len 是该段的长度(添加的整数可以为不同)。
可以证明,始终可以使所有元素等于零。
输入 第一行包含一个整数 n (1≤ n ≤100000):数组元素的数量。
第二行包含以空格分隔的数组 a 的 n 个元素: a1 , a2 ,… , an (− 10 ^ 9≤ ai ≤10 ^ 9)。
输出 输出应包含代表三项操作的六行。
对于每个操作,打印两行:
第一行包含两个整数 l , r (1≤ l ≤ r ≤ n ):选定线段的边界。 第二行包含 r - l +1个整数 bl , bl + 1,…, br (−10 ^ 18≤ bi ≤10 ^ 18):要添加到 al , al +1,...的数字, ar ; bi应该被 r − l +1整除。
示例
输入
4
1 3 2 4
输出
1 1
-1
3 4
4 2
2 4
-3 -6 -6
代码解决方案
#include<bits/stdc++.h>
int64_t minv(int64_t a,int64_t m) {
assert(0 < a && a < m);
if (a == 1) return 1;
return m - minv(m % a,a) * m / a;
}
int main() {
using namespace std;
ios_base::sync_with_stdio(false),cin.tie(nullptr);
int N; cin >> N;
vector<int64_t> A(N);
for (int i = 0; i < N; i++) {
cin >> A[i];
}
if (N == 1) {
cout << 1 << ' ' << 1 << '\n' << -A[0] << '\n';
cout << 1 << ' ' << 1 << '\n' << 0 << '\n';
cout << 1 << ' ' << 1 << '\n' << 0 << '\n';
exit(0);
}
int64_t invNm1 = minv(N-1,N);
assert(invNm1 * (N-1) % N == 1);
vector<int64_t> op1(N);
vector<int64_t> op2(N-1);
for (int i = 0; i < N-1; i++) {
op2[i] = -A[i] % N * invNm1 % N;
assert((A[i] + (op2[i] * (N-1))) % N == 0);
op1[i] = -(A[i] + (op2[i] * (N-1))) / N;
assert(A[i] + (op1[i] * N) + (op2[i] * (N-1)) == 0);
}
cout << 1 << ' ' << N << '\n';
for (int i = 0; i < N; i++) {
cout << op1[i] * N << " \n"[i+1==N];
}
cout << 1 << ' ' << N-1 << '\n';
for (int i = 0; i < N-1; i++) {
cout << op2[i] * (N-1) << " \n"[i+1==N-1];
}
cout << N << ' ' << N << '\n';
cout << -A[N-1] << '\n';
return 0;
}
问题
我相信代码对minv使用了扩展的欧几里得算法,我确实需要帮助来可视化minv部分的情况。另外,我在遵循op2和op1方程的for循环中的逻辑以及代码如何从提供的输入中产生输出时遇到麻烦。有人在这里与方程式和逻辑分享对代码的解释吗?
解决方法
minv
函数可找到模乘逆。意思是,对于给定的数字a
,其反模N
是满足b
的数字a*b % N = 1
。
Extended Euclidian algorithm can be used to find such inverse.
至:
cout << op1[i] * N << " \n"[i+1==N];
这只是索引的骗局。 "<space>\n"
的类型为 char[2]
char[3]
,因此可以使用索引在空格和换行符之间进行选择。由于布尔可以隐式转换为int
-true->1
,false->0
,因此布尔可用于为此类数组建立索引。