多校第五場的題。
首先是一個好數只在某個進制下,不會是在兩個進制下都爲好數。
另外每個進制好數的個數爲d!-(d-1)!,因爲要保證第一位不爲0.
然後就是在臨界進制下有多少個好數的問題,可以變成兩個子問題,一個是找到小於n的第一個排列數,第二個是排列數的排名。
康託展開修改一下就可以變成求這兩個問題。
具體來說就是對於某個位的數字,比它小且不在前面出現的數字作爲當前位時後面無論怎麼擺都會小於這個數,so個數×剩下位數的階乘就是排名再增加的。
最後一位要+1,第一位不能爲0。區別在於如果現在的數字是前面出現過的數字,直接break掉。
比賽的時候口胡了一下,後來覺得麻煩就否決掉了23333.
其實沒有多複雜。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstring> #include<algorithm> #include<cstdio> #include<cmath> #define rep(i,l,r) for(int i=l;i<=r;i++) #define dow(i,l,r) for(int i=r;i>=l;i--) #define rep0(i,r) for(int i=0;i<r;i++) #define repedge(i,x) #define maxn 50010 #define LL long long using namespace std; const LL mm=998244353; int a[maxn],b[maxn],c[maxn],num[maxn],n,len,len2; LL f[maxn]; char s[maxn]; double maxd[maxn]; void change(char *s) { len=strlen(s); rep0(i,len) a[len-i-1]=s[i]-'0'; } void small(int *a) { a[0]--; rep(i,0,len-1) if (a[i]<0) { a[i+1]--; a[i]+=10; } else break; while (len && !a[len-1]) len--; } void tobe(int m) { len2=0; rep0(i,len) c[i]=a[i]; int len1=len; while (len1) { int now=0; dow(i,0,len1-1) { now=(now*10+c[i]); c[i]=now/m; now%=m; } b[len2++]=now; if (len2>m) break; while (len1 && !c[len1-1]) len1--; } } LL ask(int m) { tobe(m); if (len2>m) return (f[m]-f[m-1]+mm)%mm; if (len2<m) return 0; rep0(i,m) num[i]=0; LL ans=-f[m-1]; dow(i,0,m-1) { rep0(j,b[i]) if (!num[j]) ans=(ans+f[i])%mm; if (num[b[i]]) break; num[b[i]]=1; if (!i) ans++; } return ans; } LL calc() { LL sum=0; int now; for(now=2;maxd[now+1]<=len-1;now++); if (now>2) sum=(f[now-1]-1+mm)%mm; LL more=ask(now)+ask(now+1); sum=(sum+more)%mm; return sum; } int main() { int tt; // freopen("1009.in","r",stdin); // freopen("1.out","w",stdout); scanf("%d",&tt); f[0]=1; for(int i=1;i<=maxn;i++) { f[i]=f[i-1]*(LL)i%mm; maxd[i]=log(i)*(i-1)/log(10); } while (tt--) { scanf("%s",s); change(s); small(a); LL ans=-calc(); scanf("%s",s); change(s); ans=((ans+calc())%mm+mm); printf("%lld\n",ans%mm); } return 0; }