AcWing 2068. 整数拼接--(m-a[i]%m)%m的解释
原题链接
中等
作者:
B1ackGod
,
2021-02-15 02:05:56
,
所有人可见
,
阅读 3242
核心:通过预处理优化枚举,降低算法复杂度
关于代码中的(m-t)%m即(m-a[i]%m)%m是什么意思?
- 由题意知道,我们用序列中的a[i]、a[j]来拼接出一个数,且i和j不相同,a[i]在前在后是不同的选法。
- 例如a[j]在前,a[i]在后,即a[j]*(10^leni) + a[i],leni表示a[i]的位数。
- 当这个数对m取模为零,即
(a[j]*(10^leni)+a[i])%m==0
那么我们就可以得到 a[j] * (10^leni)%m = (-a[i])%m
注意:这里的-a[i]为负数
我们知道对负数取模,在c++会得到负数。例如(-5)%3=-2,因此我们写成(3+(-5)%3)%3=1,就可以得到正数。
最重要的来了, 5%3=2 所以,3-5%3 = (3+(-5)%3)%3=1
因此代码中(m-a[i]%m)%m其实就是利用(正的)a[i]求得(-a[i])%m。
- 代码中预处理求的是a[j] * (10^leni)%m的余数出现的次数。然后我们枚举每一个a[i],利用(m-t)%m求出(-a[i])%m,根据(-a[i])%m查找预处理出来的表。
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int s[11][N];
int a[N];
int n,m;
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++) //预处理
{
ll t=a[i]%m;
for(int j=0;j<11;j++)
{
s[j][t]++;
t=t*10%m;
}
}
ll res=0;
for(int i=0; i<n; i++){
ll t = a[i]%m; //求a[i]的余数
int len = to_string(a[i]).size();
res += s[len][(m-t)%m];
ll r = t;
while(len--) r=r*10%m; //求a[j]的余数,同上面的预处理求法一样
if(r==(m-t)%m) res--;
}
cout<<res;
return 0;
}
用不到s[0][t]啊
这里的t要小于m,才能使用(m-t)%m,例如,t=4,m=3,(3-4)%3,还是为-1,如果使用(m+t%m)%m就不存在这样的问题(3-4%3)%3=2,所以两者要等价使用,必须t<m
t不就是a[i]%m吗,t已经小于m了啊
为啥要枚举0呢,而且不枚举0还会出错,为什么为什么为什么TAT???
为啥要res–
谢谢大佬,看完恍然大悟
说实话,很想知道,为什么上来直接取
LL t = a[i] % m
不会有问题呢?为什么可以直接这样取值呢?我也想知道
没问题的,数论里边的内容,同余式具有传递性:
若a≡b(mod m),b≡c(mod m),则a≡c(mod m);
所以先取余和后取余差别不大
大佬,请问从 (a[j]*(10^leni)+a[i])%m==0 变为 a[j] * (10^leni)%m = (-a[i])%m 利用的是取模运算规律嘛?? 新手小白,不太懂这个变换过程。因为只知道(a + b) % p = (a%p + b%p) %p = 0,如何可以变得出来呀??
数论里边的内容, a[j](10^leni)+a[i]与0模m同余,即 a[j](10^leni)+a[i] = 0 (mod m), 移项得 a[j] * (10^leni)%m = -a[i] (mod m) ,这里等号应为同余号
好的,懂啦,谢谢大佬
(m-t)%m=m-t 这两个不是相等的吗
t已经是t%m了
你注意一下这里是对它的负数取余,哈哈