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

2017 Multi-University Training Contest - Team 5:Rikka with Number

2017-08-11 17:22 344 查看
点击打开题目链接


Rikka with Number

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 409    Accepted Submission(s): 126


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),A1≠0) is
good if and only A1−Am is
a permutation of numbers from 0 to d−1.

A number K is
good if and only if there exists at least one d≥2 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(1≤t≤20),
the number of the testcases. 

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

 

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

 

Source

2017 Multi-University Training Contest - Team 5

 

题目意思:

一个数K,如果表示成d进制(d>=2)并且表示成d进制后是由(0~d-1)的排列组成的(前导不能是0)。那么K就是一个good number。

只要存在任意一个d进制,能使数K满足上面说的条件,那么数k就是D进制。

然后给出T组测试数据,让求L到R区间里面有多少个goodnum。

思路来源:http://www.zhimengzhe.com/bianchengjiaocheng/cbiancheng/356607.html

他是用java的大数类写的,我看了他的思路后,他的有些代码我看不懂,所以在看不懂的地方我就按我的想法写了。

第一次用java交题,错了好多次,从eclipse里面粘出来了后,一直带着package 包名,提交,提交了十几次都Wa了。到后来才意识到自己带着package这句话提交了。所以用java写的时候,提交要小心。

现在就来说解题思路吧:

首先说明两点:

假如当前我们要找的是是d进制组成的good number。首先我们知道good number用d进制表示,必须有d位。

而且是0~d-1的一个散列,所以d进制构成的good number总共有:

第一位不能是0,所以第一位可以选择:d-1 ,d-2,........,3,2,1。总共有d-1种选择。

选了第一位后,第二位带上有d-1种选择。

选了第二位后,除掉两个数字,第三位有d-2种选择。

选了第三位后,除掉三个已经选择的数字,第三位有d-3种选择。

因此d进制构成的good number总共有(d-1)*fac(d-1)    PS:fac(x) 代表x的阶乘。

在这么多符合条件的数字种。

10234.....d-3,d-2,d-1   是d进制构成的good number中最小的那个数。记作:Min_d;

d-1,d-2,d-3,.....3,2,1,0  是d进制构成的good number中最大的那个数。仅作:Max_d;

例如6进制中的最小good numbers是:六进制(102345),最大的good number是:六进制 (543210)

因此我们可以打表,先算出每种进制构成的good number中最大的那个数。

接着对于题目给定的区间(L,R)。

我们可以通过二分查询,查找第一个good number种的最大数大于L的进制,记为:left

同样查找第一个good number种最大数大于R的进制,记作:right.

可以看下图,情况比较多。



我们可以看到图中有三种情况,第一个图,先找到了相应的left进制和right进制,由于left进制下的goodnum最

大值和最小值,可能横跨L,那么left进制中共有(left-1)*fac(left-1)这个多个goodnum就不全都位于所求区间。

同理right进制的数,也可能横跨R,那么right进制中共有(right-1)*fac(right-1)这么多个goonum就不全都位于

所求的区间。因为我们先不来考虑横不横跨边界的情况,则我们可以考虑left+1 进制~right-1进制下的goodnum

一定是位于L,R这个区间的。前提条件是(left+1)<=right-1。因为如果left+1>right-1。这样是不合理的。

所以我们先计算left+1进制到right-1进制下goodnum的个数。

ans = 0;

for(int i = left+1; i <= right-1; i++)

ans += (i-1)*fac(i-1);

然后呢我们就要求L~Max_left(Max_left是left进制下最大的那个goodnum)中有多少个goodnum。

求解过程是这样的,我们需要在left进制下求L的每一位:

最高位的权值是weight = left^(left-1)。所以如果L/weight向下取整答案是0的话。则说明Min_left是在L的

右侧,如第二个图哪种情况所示,因此,left进制中的所有goodnum也都是答案的一部分,那么求出

这部分,就可以结束了。如果当前最高位不是0。求解过程如下所示:

举个例子:如果L化成6进制是 524301的话。则425301 ~ 543210中的答案数量是这样计算的。

L 除以 6^5后,得到最高位是4...,并标记4,是用过的。

大于4且没有标记的数为5,则5开头的全排列都是满足条件的。

L= L%6^5

L除以6^4后,得到次高位:2。  对于前面固定的一位数字4,

次高位可以选择没有用过的数字3,5,然后根剩余的数全排列。

每次都这样计算。。。。。直到处理到最低位:

同理可以用这个方法,求除R~Max_right中的right进制goodnum数量。这样的话可以用right进制下

所有的goodnum数量减去这个数字,就得到Min_right~R中的right进制goodnum数量。

还有一种情况就是L~R这个区间比较短,那么left和right会是同一个数,这时候的答案

就应该直接等于L~Max_left的goodnum数量-R~Max_right的goodnum数量。

易错点:

代码中会包含先相减后取模,由于参与运算的数已经是取过模的数,所以相减的时候有可能会得到负数,

此时应该先加上mod后再对mod取模。

具体详情请见代码:

import java.math.BigInteger;
import java.util.Scanner;
public class Main {
static int maxn = 1600;
static BigInteger[] base = new BigInteger[maxn];  ///base[i]进制下的最大goodnum数组
static int mod = 998244353;
static long[] fac = new long [maxn];  ///存放数的阶乘
static void Init() {
base[0] = BigInteger.ZERO;
base[1] = BigInteger.ZERO;
/*i代表进制,i进制,0~i-1排列出的最大数字是i-1,i-2,i-3,....3,2,1,0,计算
每个进制中的这个最大数。*/
for(int i = 2; i < maxn; i++) {
base[i] = BigInteger.ZERO;
for(int j = i-1; j >= 0; j--) {
base[i] = base[i].multiply(BigInteger.valueOf(i)).add(BigInteger.valueOf(j));
}
}
fac[0] = fac[1] = 1;  ///0的阶乘和1的阶乘的值为1.
/*i进制由0~i-1组成的排列数为i的阶乘种*/
for(int i = 2; i < maxn; i++){
fac[i] = fac[i-1]*i%mod;
}
}
/*找到第一个最大排列值大于num的i进制排列*/
static int binarySearch(BigInteger num) {
int low = 0,high = maxn-1,mid;
while(low < high) {
mid = (low+high)/2;
if(base[mid].compareTo(num)>=0) high = mid;
else low = mid+1;
}
return high;
}
public static void main(String[] args) {
Init();
Scanner input = new Scanner(System.in);  ///java中的输入
int t = input.nextInt();
int[] vis = new int[maxn];
while(t!=0) {
t--;
BigInteger L,R;
L = input.nextBigInteger();
R = input.nextBigInteger();
int leftbase,rightbase;
leftbase = binarySearch(L);
rightbase = binarySearch(R);
leftbase++;
rightbase--;
long ans;
if(leftbase>rightbase) {
ans = 0;
}else {
ans = 0;
for(int i = leftbase; i <= rightbase; i++)
ans = (ans + (i-1)*fac[i-1]%mod)%mod;
}
leftbase--;
rightbase++;
///求L到leftbase符合要求的数
BigInteger weight = (BigInteger.valueOf(leftbase)).pow(leftbase-1);
for(int i = 0; i < maxn; i++) {
vis[i] = 0;
}
long ans1 = 0;
for(int i = leftbase-1; i >= 0; i--) {
int top = L.divide(weight).intValue();
if(top == 0 && i == leftbase-1) {
ans1 = (ans1 + (leftbase-1)*fac[leftbase-1]%mod)%mod;
break;
}
int cnt = 0;
for(int j = top+1; j < leftbase; j++) {
if(vis[j]==0)
cnt++;
}
ans1 = (ans1 + cnt*fac[i]%mod)%mod;
if(vis[top]==0 && i == 0) ans1 = (ans1+1)%mod;
if(vis[top]==1) break;
L = L.mod(weight);
vis[top] = 1;
weight = weight.divide(BigInteger.valueOf(leftbase));
}
long ans2 = 0;
for(int i = 0; i < maxn; i++) {
vis[i] = 0;
}
weight = (BigInteger.valueOf(rightbase)).pow(rightbase-1);
for(int i = rightbase-1; i >= 0; i--)
{
int top = R.divide(weight).intValue();
if(top == 0 && i == rightbase-1) {
ans2 = (ans2 + (rightbase-1)*fac[rightbase-1]%mod)%mod;
break;
}
int cnt = 0; ///统计没标记过的且大于top的数字个数。
for(int j = top+1; j < rightbase; j++) {
if(vis[j]==0)
cnt++;
}
ans2 = (ans2 + cnt*fac[i]%mod)%mod;
if(vis[top]==1) break;
R = R.mod(weight);
vis[top] = 1;
weight = weight.divide(BigInteger.valueOf(rightbase));
}
if(leftbase == rightbase) {
ans = ((ans1-ans2)%mod+mod)%mod;
}
else
{
long tmp = (((rightbase-1)*fac[rightbase-1]-ans2)%mod+mod)%mod;
ans = (ans + ans1 + tmp)%mod;
}
System.out.println(ans);
}
input.close();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: