您的位置:首页 > 大数据 > 人工智能

HDU 6093 Rikka with Number (2017 Multi-Univ Training Contest 5)

2017-08-14 14:56 423 查看

Problem

d (d≥2)进制下的
好数
被定义为:K=(A1A2...Ad)d , 其中 Ai≠Aj 且 0≤Ai≤d−1 ,同时 A1≠0

求区间 [L,R] 中多少数恰好是 d 进制下的好数 ?

Limit

1≤L≤R≤105000

L,R 为十进制数

Idea

d 进制的最大好数值为 (d−1)⋅dd−1+(d−2)⋅dd−2+⋯+0⋅d0

d 进制的最小好数值为 1⋅dd−1+0⋅dd−2+2⋅dd−3+⋯+(d−1)⋅d0

故 d-1 进制下的最大好数与 d 进制下的最小好数满足:

(d−2)⋅(d−1)d−2+(d−3)⋅(d−1)d−3+⋯+0⋅(d−1)0<1⋅dd−1+0⋅dd−2+2⋅dd−3+⋯+(d−1)⋅d0

即 d 进制的好数区间与 d-1 进制的好数区间不会重叠。

故具体做法为:

STEP 1. 分别处理子问题 [1, L] 和 [1, R] 中有多少满足条件的好数

STEP 2. 二分枚举最大的 d 进制满足 d 的右区间仍 ≤N (N 作为区间的右值 [1, N]) 。

STEP 3. 在二分后,处理 d+1 进制中有多少满足条件的好数,额外添加即可。

STEP 4. 在处理 d+1 进制时,优先将 N 转换为 d+1 进制的数组,dfs 处理最大的 d+1 进制数。利用康托展开计算个数。

Code

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

public class HDU_6093 {

public static void main(String[] args) throws IOException {
SolverHDU_6093 solver = new SolverHDU_6093();
solver.run();
}
}

class SolverHDU_6093 {

private static final int MOD = 998244353;
private static final int N = 5000;
private int t;
private BigInteger L, R;
private long[] factorial = new long
;
private int[] N2d = new int
;
private boolean[] vis = new boolean
;
private int[] maxDNum = new int
;
private int[] bit = new int
;

int lowbit(int x) {
return x & -x;
}

void add(int x) {
for (int i=x;i<N;i+=lowbit(i)) {
bit[i]++;
}
}

int get(int x) {
int res = 0;
for (int i=x;i!=0;i-=lowbit(i)) {
res += bit[i];
}
return res;
}

void init() {
factorial[0] = 1;
for (int i=1;i<N;i++) {
factorial[i] = factorial[i-1] * i % MOD;
}
}

boolean dfs(int idx, int d) {
if (idx > d)    return true;
if (vis[ N2d[idx] ] == true) {
for (int p=N2d[idx];p>=0;p--) {
if (vis[p] == false) {
vis[p] = true;
maxDNum[idx] = p;
break;
} else if (p == 0) {
return false;
}
}
for (int i=idx+1, p=d-1;i<=d;i++) {
while (vis[p])  p--;
maxDNum[i] = p;
vis[p] = true;
}
return true;
} else {
maxDNum[idx] = N2d[idx];
vis[ N2d[idx] ] = true;
if (dfs(idx+1, d) == false) {
vis[ N2d[idx] ] = false;
if (N2d[idx] == 0)  return false;
for (int p=N2d[idx]-1;p>=0;p--) {
if (vis[p] == false) {
vis[p] = true;
maxDNum[idx] = p;
break;
} else if (p == 0) {
return false;
}
}
for (int i=idx+1, p=d-1;i<=d;i++) {
while (vis[p])  p--;
maxDNum[i] = p;
vis[p] = true;
}
return true;
} else {
return true;
}
}
}

long calcExt(BigInteger N, int d) {
BigInteger NN = N;
for (int i=d;i!=0;i--) {
N2d[i] = (int) NN.mod(BigInteger.valueOf(d)).longValue();
NN = NN.divide(BigInteger.valueOf(d));
}
if (N2d[1] == 0)    return 0;

for (int i=0;i<=d;i++)  vis[i] = false;
vis[ N2d[1] ] = true;
maxDNum[1] = N2d[1];
if (dfs(2, d) == false) {
if (N2d[1] - 1 == 0)    return 0;
vis[ N2d[1] ] = false;
vis[ N2d[1] - 1 ] = true;
maxDNum[1] = N2d[1] - 1;
int p = d-1;
for (int i=2;i<=d;i++) {
while (vis[p] == true) p--;
maxDNum[i] = p;
vis[p] = true;
}
}

long ans = 0, dig;
for (int i=1;i<=d;i++)  bit[i] = 0;
for (int i=d;i!=0;i--) {
dig = get(maxDNum[i]);
add(maxDNum[i] + 1);
if (i == 1) dig--;
ans = (ans + dig * factorial[d-i]) % MOD;
}
return (ans+1) % MOD;
}

long calc(BigInteger N) {
int l = 2, r = 2000, mid, d = 1;
BigInteger maxNumInD;
while (l <= r) {
mid = (l+r) / 2;
maxNumInD = BigInteger.ZERO;
for (int j=mid-1;j>=0;j--) {
maxNumInD = maxNumInD.multiply(BigInteger.valueOf(mid));
maxNumInD = maxNumInD.add(BigInteger.valueOf(j));
}
if (maxNumInD.compareTo(N) <= 0) {
l = mid + 1;
d = mid;
} else {
r = mid - 1;
}
}
if (d == 1) return 0;
long ans = 0;
for (int i=2;i<=d;i++) {
ans += factorial[i] - factorial[i-1];
ans %= MOD;
}
ans += calcExt(N, d+1);
ans %= MOD;
return ans;
}

void run() throws IOException {
init();
Scanner cin = new Scanner(System.in);
t = cin.nextInt();
for (int ica=1;ica<=t;ica++) {
L = cin.nextBigInteger();
R = cin.nextBigInteger();

long left = calc(L.subtract(BigInteger.ONE));
long right = calc(R);
System.out.println((right - left + MOD) % MOD);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐