2021-2022 ACM-ICPC Brazil Subregional Programming Contest C Creating Multiples
作者:互联网
目录
知识点:同余、逆元、拓欧
题意
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)
n≡m(mod b+1)⟺n−m≡0(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)
ci(ci为新数)<ai(ai为原数)且(ai−ci)×bi≡m(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)
ai−ci≡m×(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;
}
标签:Brazil,arr,return,Creating,Contest,ll,逆元,sum,mod 来源: https://blog.csdn.net/qq_19315559/article/details/121220655