NOI模拟 GCD

涉及知识点:数位DP,容斥原理

题意

\(\text{dig}(i)\) 表示 \(i\) 十进制表示下各数位乘积,则一个数对是正确的当且仅当满足以下条件:

  • \(1 \leq i,j \leq n\)
  • \(\text{dig}(i) \times \text{dig}(j) > 0\)
  • \(\gcd(\text{dig}(i), \text{dig}(j)) \leq k\)

给你 \(n,k\ (\leq 10^{18})\) 求有多少正确的有序数对。答案对 \(998244353\)​​ 取模。

思路

由于 \(\text{dig}\) 为“数位”的乘积,我们知道一个 \(1\leq x\leq 9\) 的数的质因子只有可能为 \(2,3,5,7\),因此乘起来质因子也只有这四个,而且发现这四个质因子个数的有效组合其实只有 \(10^4\) 级别。

\(f_{i,j,k,l}\) 为满足 \(2^i \times 3^j \times 5^k \times 7^l | \text{dig}(a)\) 且满足 \(a\leq n\)\(a\)​​ 的个数。这东西可以数位 DP。

那么满足 \(\gcd(\text{dig}(a),\text{dig}(b))=2^i \times 3^j \times 5^k \times 7^l\)\((a,b)\) 有序数对的个数为:

\[\large\sum\limits_{p_1, p_2, p_3, p_4 \in {0,1}} f_{i+p_1,j+p_2,k+p_3,l+p_4}^2 \times (-1)^{p_1 + p_2 + p_3 + p_4} \]

这是个容斥式,\(f_{i,j,k,l}\) 意味着 \(x=2^{x_2} \times 3^{x_3} \times 5^{x_5} \times 7^{x_7}(x_2\geq i,x_3\geq j,x_5\geq k,x_7\geq l),x\leq n\) 的情况数。

\(a=2^{a_2} \times 3^{a_3} \times 5^{a_5} \times 7^{a_7}, b=2^{b_2} \times 3^{b_3} \times 5^{b_5} \times 7^{b_7}\) 时,我们通过上述容斥式算出了 \(\min(a_2,b_2)=i,\min(a_3,b_3)=j,\min(a_5,b_5)=k,\min(a_7,b_7)=l\) 的情况数。具体做法可以理解为用 \(f_{i,j,k,l}\) 减去了 \((i+1,j,k,l),(i,j+1,k,l),(i,j,k+1,l),(i,j,k,l+1)\) 这几种情况的并集(也就是 \(a,b\) 存在质因子对应的指数取 \(\min\) 大于我们想要的情况)。

我们又知道 \(\gcd(a,b)=2^{\min(a_2,b_2)} \times 3^{\min(a_3,b_3)} \times 5^{\min(a_5,b_5)} \times 7^{\min(a_7,b_7)}\),因此就算出来了满足 \(\gcd(\text{dig}(a),\text{dig}(b))=2^i \times 3^j \times 5^k \times 7^l\)\((a,b)\) 有序数对的个数。

最后遍历一下质因子之积 \(\leq k\) 的所有组合统计答案即可。

代码

数位 DP 部分较为容易,直接看代码吧。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL P=998244353;
const int MAXDIG=20;
LL n,K,ans=0,f[MAXDIG][61][39][27][23];
bool vis[MAXDIG][61][39][27][23];
int num[MAXDIG],numlen=0;
LL dfs(int dep,bool zero,bool limit,int i,int j,int k,int l){
    if(i/3+j/2+k+l>dep) return 0;//剪枝
    if(dep==0) return !zero && !i && !j && !k && !l;
    if(!zero && !limit && vis[dep][i][j][k][l]) return f[dep][i][j][k][l];
    int dig=limit?num[dep]:9;
    LL res=0;
    if(zero) res=(res+dfs(dep-1,true,limit&&(dig==0),i,j,k,l))%P;
    for(int p=1;p<=dig;p++){
        res=(res+dfs(dep-1,false,limit&&(dig==p),max(i-(!(p%2))-(!(p%4))-(!(p%8)),0),max(j-(!(p%3))-(!(p%9)),0),max(k-(!(p%5)),0),max(l-(!(p%7)),0)))%P;
    }
    if(!zero && !limit){
        vis[dep][i][j][k][l]=true;
        f[dep][i][j][k][l]=res;
    }
    return res;
}
LL calc(int i,int j,int k,int l){
    LL res=0;
    for(int p2=0;p2<=1;p2++)
    for(int p3=0;p3<=1;p3++)
    for(int p5=0;p5<=1;p5++)
    for(int p7=0;p7<=1;p7++){
        LL tmp=dfs(numlen,true,true,i+p2,j+p3,k+p5,l+p7);
        res=(res+1LL*((p2+p3+p5+p7)%2?P-1:1)*tmp%P*tmp%P)%P;
    }
    return res;
}
int main(){
    // freopen("b.in","r",stdin);
    // freopen("b.out","w",stdout);
    cin>>n>>K;
    while(n){
        num[++numlen]=n%10;
        n/=10;
    }
    LL tmp2,tmp3,tmp5,tmp7;
    tmp2=1;
    for(int i=0;tmp2<=K;i++,tmp2*=2){
        tmp3=1;
        for(int j=0;tmp2*tmp3<=K;j++,tmp3*=3){
            tmp5=1;
            for(int k=0;tmp2*tmp3*tmp5<=K;k++,tmp5*=5){
                tmp7=1;
                for(int l=0;tmp2*tmp3*tmp5*tmp7<=K;l++,tmp7*=7){
                    ans=(ans+calc(i,j,k,l))%P;
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2024-05-24 17:10  MessageBoxA  阅读(5)  评论(0编辑  收藏  举报