ICPC2022区域赛杭州站 A

problem A  Modulo Ruins the Legend

数论+扩展欧几里得算法

题目大意:

给定一个数列a,求出俩个未知数 s, d代表等差数列的首项及公差 ,满足将数列每一项于等差数列每一项相加构成的新数列的和于m取模后最小值

常规思路:

        根据题目大意我们可以得到

1、 \sum_{i=1}^{n}a +n*s+n*(n+1)/2*d  

对于此时的二元一次不定方程根据欧几里得性质有 

2、 n*s+n*(n+1)/2*d  =k2 * gcd(n,n(n+1)/2)(k2\epsilon 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 \epsilon 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;
}

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenRenning

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值