problem A Modulo Ruins the Legend
数论+扩展欧几里得算法
题目大意:
给定一个数列a,求出俩个未知数 s, d代表等差数列的首项及公差 ,满足将数列每一项于等差数列每一项相加构成的新数列的和于m取模后最小值
常规思路:
根据题目大意我们可以得到
1、
a +n*s+n*(n+1)/2*d
对于此时的二元一次不定方程根据欧几里得性质有
2、 n*s+n*(n+1)/2*d =k2 * gcd(n,n(n+1)/2)(k2
N+)
假设结果是1式结果是ans那么结合2式看得到:
3、 sum + k2 * g = ans - k1 *m
(g=gcd(n,n(n+1)),k1=(ans/m)是对结果取模的写法)
变个型交换下位置得到
k1*m + k2 *g = ans - sum
同理根据欧几里得性质得到
4、 k1*m + k2* g= z * gcd(m,g)(z
N+)=ans - sum
因为 ans取值范围【0,m-1】所以 z*gcd(m,g)取值范围为【-sum,m-1-sum】
对于最小值z即为 -sum/gcd(m,g)
因为m已知、g已知、sum已知因此z(min)可得
所以ans = z*gcd(m,g)+sum
根据exgcd可得到 k1、k2值,已知k2根据2式子再进行一次exgcd即可求得 s和 d
/*
* problem A
* 2023/7/15
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n, m;
ll gcd(ll a, ll b) {
return b ? gcd(b, a % b) : a;
}
ll exgcd(ll a, ll b, __int128& x, __int128& y) {
if (!b) {
x = 1, y = 0;
return a;
}
ll d = exgcd(b , a%b, y, x);
y -= a / b * x;
return d;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
cin >> n >> m;
ll sum = 0;
for (int i = 0; i < n; i++) {
ll x;
cin >> x;
sum += x;
}
ll g = gcd(n, n * (n + 1)/2);
ll gg = gcd(g, m);
ll z = -sum / gg;
ll ans_sum = z * gg;
ll ans = ans_sum + sum;
__int128 k1, k2, s, d;
exgcd(m, g, k1, k2);
k1 *= z, k2 *= z;
k1 %= m, k2 %= m;
exgcd(n, n * (n + 1)/2, s, d);
s *= k2;
d *= k2;
s = (s % m + m) % m, d = (d % m + m) % m;
cout << ans << "\n" << (ll)s << " " << (ll)d;
return 0;
}