Cry_For_theMoon  

题意:

定义一个数字是好的,当且仅当它的各位数字之积与其本身的 \(\gcd>1\)

\(T\) 次询问,每次给定 \(L\le R\),问 \([L,R]\) 中有多少个数字是好的。

注意:\(\gcd(0,0)\) 不存在。\(\gcd(0,x\gt 0)=x\)

\(T=10^4,1\le L\le R\le 10^18\)

分析:

一眼数位dp没问题。相当于问 \(\le x\) 的好数有多少个。

容易想到朴素的 dp:设 \(f(i,m2,m3,m5,m7,mask,0/1)\) 表示从高往低确定了 \(i\) 位,是否已经小于 \(x\),然后当前部分模 \(2,3,5,7\) 的余数分别为 \(m2,m3,m5,m7\),而各位数字包含的质因数集合为 \(mask\)。乘上转移后单次复杂度是 \(9\times 18\times 2^4\times 2\times 210(2\times 3\times 5\times 7)\)。看上去很优秀,\(10^4\) 组数据后 T 飞。

感觉每次都做一遍 dp 没有前途,能不能预处理一些信息,每次询问快速统计。

正整数里,如果带 \(0\) 的那么一定合法(忽略前导零),所以带 \(0\) 的是好统计的。我们仅考虑所有数字都不出现 \(0\) 的情况。

分类讨论:设 \(x\) 的位数为 \(cnt\)。首先我们预处理位数小于 \(cnt\) 的答案,这个容易 dp 预处理然后统计。然后考虑位数 \(=cnt\) 的。

不妨令数 \(\lt cnt\) 而不是 \(\le cnt\),特判一下 \(cnt\) 是否合法即可。

套路地枚举前缀重合部分,和下一位的数字(小于 \(x\) 上该位数字且不为 \(0\))(注意前缀重合部分也不能有 \(0\))。

然后我们更改 dp 状态,\(f(i,m2,m3,m5,m7,mask)\) 是从低往高选 \(i\) 位,\(m2,m3,m5,m7,mask\) 的定义不变。注意到这里的 dp 我们不用再去考虑是否 \(\lt x\) 这件事情了。

设前缀重合部分长度为 \(num\),由于我们知道前缀(还有暴力枚举的下一位)的信息(就是说余数和数码的质因数集合),那么我们可以和 \(dp(cnt-num-1)\) 的信息合并。

如果暴力枚举的话,总的复杂度依旧是很高的...... 我们考虑再把这个东西预处理出来:\(g(i,m2,m3,m5,m7,mask)\) 代表我们还要选择末尾的 \(i\) 位数字,而前缀部分的信息是 \(m2,m3,m5,m7,mask\) 的个数。

考虑计算 \(g\)\(16^2\) 的枚举后 \(i\) 位数字的质因数信息,和最后符合条件(指 \(m=0\) 且质因数集合里它也出现)的质因数,然后此时的信息再用一个 \(h\) 数组预处理...... 处理出来以后直接根据枚举的,后 \(i\) 位质因数集合的 \(size\) 的奇偶进行容斥就好了。

算一下发现预处理计算量大概 \(3e7\),而最后单次询问的计算量就只有 \(18\times 9\) 了,所以 \(T\) 其实可以开到 \(5\times 10^5\)......

//https://codeforces.com/gym/103462/problem/D
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
using namespace std;
const int MAXN=20;
ll f[MAXN][2][3][5][7][16],g[MAXN][2][3][5][7][16],h[MAXN][3][4][6][8][16];

ll dp[MAXN];
ll power[10][MAXN],power9[MAXN],pcnt[16];
int state[10]={15,0,1,2,1,4,3,8,1,2};
int N(int x,int mod){
    if(!x)return x;
    return mod-x;
}
int check(int m2,int m3,int m5,int m7,int mask){
    if(!m2 && (mask&1))return 1;
    if(!m3 && (mask&2))return 1;
    if(!m5 && (mask&4))return 1;
    if(!m7 && (mask&8))return 1;
    return 0;
}
void pre(){
    rep(i,1,15)pcnt[i]=pcnt[i^lowbit(i)]+1;
    power[0][0]=1;rep(i,1,19)power[0][i]=power[0][i-1]*10;
    power9[0]=1;rep(i,1,19)power9[i]=power9[i-1]*9;
    rep(mod,1,9){power[mod][0]=1;rep(i,1,19)power[mod][i]=power[mod][i-1]*10%mod;}
    f[0][0][0][0][0][0]=1;
    rep(i,0,18)rep(m2,0,1)rep(m3,0,2)rep(m5,0,4)rep(m7,0,6)rep(mask,0,15)rep(d,1,9){
        f[i+1][(m2+d*power[2][i])%2][(m3+d*power[3][i])%3][(m5+d*power[5][i])%5]\
        [(m7+d*power[7][i])%7][mask|state[d]]+=f[i][m2][m3][m5][m7][mask];
    }
    rep(i,0,18)rep(m2,0,1)rep(m3,0,2)rep(m5,0,4)rep(m7,0,6)rep(mask,0,15)if(check(m2,m3,m5,m7,mask))dp[i]+=f[i][m2][m3][m5][m7][mask];
    rep(i,0,18)rep(m2,0,2)rep(m3,0,3)rep(m5,0,5)rep(m7,0,7)rep(mask,0,15){
        rep(r2,0,1){if(m2<2 && m2!=r2)continue;
            rep(r3,0,2){if(m3<3 && m3!=r3)continue;
                rep(r5,0,4){if(m5<5 && m5!=r5)continue;
                    rep(r7,0,6){if(m7<7 && m7!=r7)continue;
                        h[i][m2][m3][m5][m7][mask]+=f[i][r2][r3][r5][r7][mask];
                    }
                }
            }
        }
    }
    rep(i,0,18)rep(m2,0,1)rep(m3,0,2)rep(m5,0,4)rep(m7,0,6)rep(mask,0,15){
        ll& ret=g[i][m2][m3][m5][m7][mask];
        rep(mask2,0,15){
            int S=mask|mask2;
            for(int mask3=S;mask3;mask3=(mask3-1)&S){
                ll r2=(mask3&1)?(N(m2,2)):(2),
                r3=(mask3&2)?(N(m3,3)):(3),
                r5=(mask3&4)?(N(m5,5)):(5),
                r7=(mask3&8)?(N(m7,7)):(7);
                if(odd(pcnt[mask3]))ret+=h[i][r2][r3][r5][r7][mask2];
                else ret-=h[i][r2][r3][r5][r7][mask2];
            }
        }
    }

}
ll T,l,r;
int d[30],cnt;
ll solve(ll x){
    if(!x)return 0;
    ll ans=0,num=x;cnt=0;
    while(num){
        d[++cnt]=num%10;
        num/=10;
    }
    rep(len,1,cnt-2){ans+=9ll*(power[0][len]-power9[len]);}
    reverse(d+1,d+1+cnt);
    ll m2=0,m3=0,m5=0,m7=0,mask=0;
    rep(i,1,cnt-1)ans+=dp[i];
    rep(i,1,cnt){
        rep(nxt,1,d[i]-1){
            int r2,r3,r5,r7;
            r2=(m2+power[2][cnt-i]*nxt)%2;
            r3=(m3+power[3][cnt-i]*nxt)%3;
            r5=(m5+power[5][cnt-i]*nxt)%5;
            r7=(m7+power[7][cnt-i]*nxt)%7;
            int masks=mask|state[nxt];
            if(i==cnt){ans+=check(r2,r3,r5,r7,masks);}
            else ans+=g[cnt-i][r2][r3][r5][r7][masks];
            if(i==1){
                int res=0;
                rep(j,1,9){
                    if(__gcd(nxt*j,nxt*10+j)>1)res++;
                }
            }
            ans+=power[0][cnt-i]-power9[cnt-i];
        }
        if(d[i]==0){
            ll val=0;
            rep(j,i+1,cnt)val=val*10+d[j];
            ans+=val+1;
            goto END;
        }else{
            if(i!=1)ans+=power[0][cnt-i];
        }
        m2=(m2+power[2][cnt-i]*d[i])%2;
        m3=(m3+power[3][cnt-i]*d[i])%3;
        m5=(m5+power[5][cnt-i]*d[i])%5;
        m7=(m7+power[7][cnt-i]*d[i])%7;
        mask|=state[d[i]];
    }
    ans+=check(m2,m3,m5,m7,mask);
    END:;return ans;
}
void solve(){
    cin>>l>>r;
    cout<<solve(r)-solve(l-1)<<endl;
}
int main(){
    pre();
    cin>>T;
    while(T--)solve();

    return 0;
}
posted on 2022-03-24 14:49  Cry_For_theMoon  阅读(89)  评论(0编辑  收藏  举报