HDU 6093 Rikka with Number(java大数+思维)

227 篇文章 0 订阅
39 篇文章 0 订阅

传送门

Rikka with Number

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 79    Accepted Submission(s): 18


Problem Description
As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:

In radix d , a number K=(A1A2...Am)d(Ai[0,d),A10) is good if and only A1Am is a permutation of numbers from 0 to d1.

A number K is good if and only if there exists at least one d2 and K is good under radix d.

Now, Yuta wants to calculate the number of good numbers in interval [L,R]

It is too difficult for Rikka. Can you help her?  
 

Input
The first line contains a number t(1t20) , the number of the testcases.

For each testcase, the first line contains two decimal numbers L,R(1LR105000) .
 

Output
For each testcase, print a single line with a single number – the answer modulo 998244353 .
 

Sample Input
  
  
2
5 20
123456 123456789
 

Sample Output
  
  
3
114480

题目大意:
给定一个区间 [L,R] ,判断区间中有多少个好数。
x 为好数的定义:x 在 任意 d 进制下,可以表示为 [0,d1] 的排列。

解题思路:

官方题解:


首先转化成计算小于等于 N 的好数有多少个。因为 nn(n+1)n,而对于 n 进制下的任何一个好数 K,都有 nn1nn ,所以每一个进制下好数的大小区间是不相交的。

不难发现 d 进制下好数的个数为 d!(d1)!,因此我们只需要计算在临界的 d 进制下,好数的个数就可以了。关于临界的 d,可以用对数估计位数得到。

d 进制下小于等于 N 的好数个数,先讲 N 转化成 d 进制,然后做一个类似康托展开的过程就可以了,因为数据范围很小,所以每一步操作都可以暴力进行。

时间复杂度 O(|R|2)

代码:

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    static int MAXN = 1600;
    static BigInteger[] dx = new BigInteger[MAXN];
    static long MOD = 998244353;
    static long [] fac = new long [MAXN];
    static void Init(){
        dx[0] = BigInteger.ZERO;
        dx[1] = BigInteger.ZERO;
        for(int i=2; i<MAXN; i++){
            dx[i] = BigInteger.ZERO;
            for(int j=i-1; j>=0; j--){
                dx[i] = dx[i].multiply(BigInteger.valueOf(i)).add(BigInteger.valueOf(j));
            }
        }
        fac[0] = fac[1] = 1;
        for(int i=2; i<MAXN; i++) fac[i]=fac[i-1]*i%MOD;
    }
    static int Low(BigInteger x){
        int low=0, high=MAXN-1;
        while(low < high){
            int mid = (low+high)/2;
            if(dx[mid].compareTo(x)>=0)high = mid;
            else low = mid+1;
        }
        return high;
    }
    public static void main(String[] args){
        Init();
        Scanner in = new Scanner(System.in);
        int T = in.nextInt();
        for(int cas=1; cas<=T; cas++){
            int [] vis = new int [MAXN];
            for(int i=0; i<MAXN; i++) vis[i] = 0;
            BigInteger L, R;
            L = in.nextBigInteger();
            R = in.nextBigInteger();
            int dl = 0, dr = 0;
            dl = Low(L)+1;
            dr = Low(R)-1;
            long ans = fac[dr]-fac[dl-1];
            ans = (ans%MOD+MOD)%MOD;
            if(dl > dr) ans = 0;
            dl--; dr++;

            long ans2=0;
            BigInteger tmp = (BigInteger.valueOf(dl)).pow(dl-1);
            for(int i=dl; i>=1; i--){
                int top = L.divide(tmp).intValue();
                if(i==dl && top==0) { ans2 = (ans2+fac[dl]-fac[dl-1])%MOD; break; }
                int cnt=0;
                for(int o=top+1;o<dl;o++) if(vis[o]==0) cnt++;
                ans2 = (ans2+(cnt)*fac[i-1]%MOD)%MOD;
                if(vis[top] == 1) break;
                L = L.mod(tmp);
                if(i==1 && vis[top]==0) ans2=ans2+1;
                vis[top] = 1;
                tmp = tmp.divide(BigInteger.valueOf(dl));
            }

            long ans1 = 0;
            for(int i=0; i<MAXN; i++) vis[i] = 0;
            tmp = (BigInteger.valueOf(dr)).pow(dr-1);
            for(int i=dr; i>=1; i--)
            {
                int top = R.divide(tmp).intValue();
                if(i==dr && top==0) {ans1 = (ans1+fac[dr]-fac[dr-1]%MOD); break;}
                int cnt=0;
                for(int o=top+1;o<dr;o++) if(vis[o]==0) cnt++;
                ans1 = (ans1+(cnt)*fac[i-1]%MOD)%MOD;
                if(vis[top] == 1) break;
                R = R.mod(tmp);
                vis[top] = 1;
                tmp = tmp.divide(BigInteger.valueOf(dr));
            }
            ans = ans+ans2 + fac[dr]-fac[dr-1]-ans1;
            if(dl==dr) ans=ans2-ans1;
            ans = (ans%MOD+MOD)%MOD;
            System.out.println(ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值