拼接数字后求36的倍数--习题笔记(数学)

大致题意:给一列数字,1≤ai​≤10的18次方, 将两个数字以字符串的形式进行拼接 18 18 ------1818,

求36的倍数有多少(不能自己拼自己) 

法一:36实质是4和9的倍数

#include 
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 a(n);
    i64 ans = 0;
    vector 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 a(n);//开一个vector  大小是n
    vread(a.data(), a.data() + n);//输入
    auto calc = [&]()
    {
        auto sum = vec_t_create(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;
}

你可能感兴趣的:(笔记,算法,c++,学习)