大致题意:给一列数字,1≤ai≤10的18次方, 将两个数字以字符串的形式进行拼接 18 18 ------1818,
求36的倍数有多少(不能自己拼自己)
法一:36实质是4和9的倍数
#include <bits/stdc++.h>
using namespace std;
using i64=long long;
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin >> n;
vector<i64> a(n);
i64 ans = 0;
vector<i64> count(10), count_mod(9);
for (i64& ai : a) {
cin >> ai;
for (int i = 1; i <= 9; i += 1) {
count[i] += (ai % 36 * 10 + i) % 36 == 0;
//枚举以个位数结尾的情况,ai*10(这里拆开的原因是4的倍数是后两位/4==0)
}
count_mod[ai % 9] += 1;//根据9的倍数的性质,可以知道各位数之和模9为几,原数字模9就是几
//例如:19 1+9=10%9=1 19%9=1 而如果是各位数之和是9就是9的倍数(3的倍数似乎都符合这个性质)
}
for (i64 ai : a) {
if (ai < 10) {
ans += count[ai];//如果是个位数结尾
} else if (ai % 4 == 0) {//4的倍数
ans += count_mod[(9 - ai % 9)%9];
//9的倍数 注意后面还要模9是因为9-0%9==0 如果是0的话不需要补充数字
}
ans -= (ai%36) == 0;//不能相同数字 所以说如果是36的倍数,就要减去一,例如:3636 7272
}
cout << ans;
}
法二:模的性质(两个数字求和模36实际上就是每一部分分别模36求和后再模36)
int main(int argc, char const *argv[])
{
int n;
long long ans = 0;
read(n);
vec_t<long long, 1> a(n);//开一个vector 大小是n
vread(a.data(), a.data() + n);//输入
auto calc = [&]()
{
auto sum = vec_t_create<long long, 36>(0);//一个vector数组 大小是36全部设为0
for (auto &i : a)//遍历数列
{
for (int j = 0; j < 36; ++j)//将这个数字作为尾数
//枚举各个模数,用一个sum保存数列某个数字的前面有多少满足条件的
{
if ((j * (i < 10LL ? 10LL : 28LL) + i) % 36 == 0)//运用了模的性质
//如果这个数字小于10,就直接返回10(如果i做尾数是个位数,前面的数字要乘10)
//如果尾数在两位数以上,说面前面的数字要乘以10的k次方(k>=2) 而根据模的性质,每个100%36=28
//于是每个100就代表一个28,因此直接乘28就好,加上并不会爆掉,最后模36即可
{
ans += sum[j];//加上这个数字前方符合36倍数的条件有多少个
}
}
sum[i % 36]++;//先模一遍,方便后面的计算,根据模数的性质,只跟模完的数字有关
(算的是前排的数字) 即18 19---1819的18
}
};
calc();
reverse(a.begin(), a.end());//正反各一次,这样两边的情况都考虑了
calc();
println(ans);
return 0;
}