当前位置: 首页 > 工具软件 > Brazil > 使用案例 >

2021-2022 ACM-ICPC Brazil Subregional Programming Contest C Creating Multiples

通博实
2023-12-01

知识点:同余、逆元、拓欧

题目链接

题意

b进制数最多使某一位减小使得新数是b+1的乘积,不能减输出-1,不用减输出0,否则输出减小的位的下标和减小后的新位。

思路

新数N是b+1的乘积转换为N%(b+1)=0,我们要让原数n取模为零,先算出原数的模m,根据同余的性质, n ≡ m ( m o d   b + 1 )    ⟺    n − m ≡ 0 ( m o d   b + 1 ) n\equiv m\quad (mod \ b+1) \iff n-m\equiv 0\quad (mod \ b+1) nm(mod b+1)nm0(mod b+1)
找出减小m的方法,即找出 c i ( c i 为 新 数 ) < a i ( a i 为 原 数 ) 且 ( a i − c i ) × b i ≡ m ( m o d   b + 1 ) c_i(c_i为新数)<a_i(a_i为原数)且(a_i-c_i)\times b^i \equiv m\quad(mod \ b+1) cici<ai(ai)(aici)×bim(mod b+1)(假设a,c逆序,i从0开始),就找出 a i − c i ≡ m × ( b i ) − 1 ( m o d   b + 1 ) a_i-c_i \equiv m\times (b^i)^{-1} \quad(mod \ b+1) aicim×(bi)1(mod b+1)
容易求出m,枚举i求出 b i b^i bi的逆元,与m相乘再判断得到的数字是否小于 a i a_i ai即可。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+10;

ll b,n;
ll arr[N];
ll sum[N];//实际只取sum[n]=m
ll invmuti[N];//b^i的逆元

ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0) return x=1,y=0,a;
	ll t=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return t;
}
//拓欧求逆元,a和mod已经互质(b与b+1互质(没有公因子)
//b^i肯定与b+1互质(没有公因子))
inline ll inv(ll a,ll mod)
{
	ll x,y;
	exgcd(a,mod,x,y);
	return (x%mod+abs(mod))%mod;//最小非负解
}

void solve()
{
	scanf("%lld%lld",&b,&n);
	for(ll i=0;i<n;i++) scanf("%lld",&arr[i]);
	reverse(arr,arr+n);
	ll muti=1;//b^i
	for(ll i=1;i<=n;i++)
	{
		sum[i]=(sum[i-1]+arr[i-1]*muti%(b+1))%(b+1);
		invmuti[i-1]=inv(muti,b+1);
		muti=muti*b%(b+1);
	}
	ll d=sum[n];
	if(d==0)//原数已经整除
	{
		puts("0 0");
		return;
	}
	reverse(arr,arr+n);
	for(ll i=0;i<n;i++)
	{
		ll t=d*invmuti[n-1-i]%(b+1);
		if(t>arr[i]) continue;//a[i]-c[i]大于a[i]跳过
		printf("%lld %lld\n",i+1,arr[i]-t);
		return;
	}
	puts("-1 -1");
}

int main()
{
//	ll t;
//	scanf("%lld",&t);
//	init();
//	cout<<invfact[1]<<endl;
//	while(t--)
		solve();
	return 0;
}
 类似资料: