这道题太毒瘤
一.题目
1.题目描述
从起点1开始,每次选择当前数的任意一位上加上去,问得到n的最小步数以及方案数。多组数据。
例如,从1开始得到100,有很多方法,其中有下面两种方式:
A. 1-2-4-8-16-17-18-19-20-22-24-28-36-39-48-56-62-68-76-83-91-100
B. 1-2-4-8-16-17-24-28-36-39-48-56-62-68-76-83-91-100
显然,B只需要17步。
而事实上,有两种17步的方法。
C. 1-2-4-8-16-22-24-28-36-39-48-56-62-68-76-83-91-100
2.输入
第一行一个T
接下来T行每行一个n
3.输出
对于每个n输出得到n的最小步数和方案数,方案数mod 1000000007后输出
如果无法得到输出IMPOSSIBLE
4.样例输入
3
16
100
87
5.样例输出
4 1
17 2
IMPOSSIBLE
6.数据规模
对于20%的数据n<=10^6
对于60%的数据n<=10^9
对于100%的数据n<=10^12,T<=100
二.题解
不知道大家有没有注意看它的数据范围,贼大贼大的,1e12!从这里就可以看出这道题是有多毒瘤,bzoj这道题一共就只有4个人过了。
好,现在开始讲1e9的做法:
定义f[S][a][b](0<=S<=2^9, 0<=a<=9, 0<=b<=9)
S表示一个状压的状态,它的9位中的0表示那一位对应的数字不能用,1表示那一位对应的数字能用。
a和b表示从a状态跳到b状态。
f[S][a][b]就表示从a状态跳到b状态耗费的步数。
这里我们就可以三位三位地跳,就是说从00a跳到00b,只要首先暴力预处理出100组(a,b)配对的所有情况就行了。
但这样会炸,下面讲一下1e12的做法:
我们将f定义为4维f[d][S][a][b](1<=d<=12)
前面我们只能三位三位地跳,现在我们就可以d位d位地跳。
如果我们想跳d位,就可以通过d-1转移过来,比如:
S0000a(前面S位已经确定)跳到S0000b就可以:通过S+1000a跳到S0000b
那么最后答案的步数和方案数的算法通过样例就行:
001 -> 101 ->201 -> 301 -> 401 -> 501 -> 511 -> 521 -> 531 -> 541 -> 551 -> 561 -> 571
最后懂了不一定能实践,具体要看代码:
三.Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define mod 1000000007
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3fll
#define LL long long
LL f[15][600][15][15], fs[15][600][15][15], tmp[15][15], tmps[15][15], T, n;
bool vis[600];
void get_it (LL S){
for (LL i = 1; i <= 9; i ++, S >>= 1)
vis[i] = 1 & S;
}
void init (){
memset (f, INF, sizeof f);
for (LL S = 0; S < 512; S ++){
for (LL i = 9; i >= 0; i --){
memset (vis, 0, sizeof vis);
get_it (S);
vis[i] = 1;
for (LL j = 0; j <= 8; j ++){
if (vis[j + 10 - i]) f[1][S][i][j] = 1, fs[1][S][i][j] = 1;
else{
for (LL k = 1 + i; k <= 9; k ++){
if (vis[k - i]){
if (f[1][S][i][j] > f[1][S][k][j] + 1)
f[1][S][i][j] = f[1][S][k][j] + 1, fs[1][S][i][j] = 0;
if (f[1][S][i][j] == f[1][S][k][j] + 1)
fs[1][S][i][j] = (fs[1][S][i][j] + fs[1][S][k][j]) % mod;
}
}
}
}
}
}
for (LL i = 2; i <= 12; i ++){
for (LL S = 0; S < 512; S ++){
for (LL s = 0; s <= 8; s ++){
memset (tmp, INF, sizeof tmp);
tmp[0][s] = 0, tmps[0][s] = 1;
for (LL j = 0; j <= 9; j ++){
for (LL a = 0; a <= 8; a ++){
for (LL b = 0; b <= 8; b ++){
int nS = j ? S | (1 << (j - 1)) : S;
if (tmp[j + 1][b] > f[i - 1][nS][a][b] + tmp[j][a])
tmp[j + 1][b] = f[i - 1][nS][a][b] + tmp[j][a], tmps[j + 1][b] = 0;
if (tmp[j + 1][b] == f[i - 1][nS][a][b] + tmp[j][a])
tmps[j + 1][b] = (tmps[j + 1][b] + fs[i - 1][nS][a][b] * tmps[j][a] % mod) % mod;
}
}
}
for (LL t = 0; t <= 8; t ++)
f[i][S][s][t] = tmp[10][t], fs[i][S][s][t] = tmps[10][t];
}
}
}
}
int main (){
//freopen ("inint.in", "r", stdin);
//freopen ("inint.out", "w", stdout);
init ();
scanf ("%lld", &T);
while (T --){
memset (tmp, INF, sizeof tmp);
scanf ("%lld", &n);
LL t = 0;
LL S = 0;
tmp[0][1] = 0, tmps[0][1] = 1;
for (LL i = 1000000000000ll, ti = 12; i >= 10; i /= 10, ti --){
for (LL j = 0; j < (n / i) % 10; j ++){
t = ! t;
memset (tmp[t], INF, sizeof tmp[t]);
LL nS = j ? S | (1 << (j - 1)) : S;
for (LL a = 0; a <= 8; a ++){
for (LL b = 0; b <= 8; b ++){
if (tmp[t][b] > tmp[! t][a] + f[ti][nS][a][b])
tmp[t][b] = tmp[! t][a] + f[ti][nS][a][b], tmps[t][b] = 0;
if (tmp[t][b] == tmp[! t][a] + f[ti][nS][a][b])
tmps[t][b] = (tmps[t][b] + tmps[! t][a] * fs[ti][nS][a][b] % mod) % mod;
}
}
}
if (((n / i) % 10)) S |= 1 << ((n / i) % 10 - 1);
}
for (LL i = 0; i <= 9; i ++){
memset (vis, 0, sizeof vis);
get_it (S);
vis[i] = 1;
for (LL a = 1; a <= 9; a ++){
if (vis[a] && a + i <= 9){
int j = a + i;
if (tmp[t][j] > tmp[t][i] + 1)
tmp[t][j] = tmp[t][i] + 1, tmps[t][j] = 0;
if (tmp[t][j] == tmp[t][i] + 1)
tmps[t][j] = (tmps[t][j] + tmps[t][i]) % mod;
}
}
}
if (tmp[t][n % 10] == LLINF)
printf ("IMPOSSIBLE\n");
else
printf ("%lld %lld\n", tmp[t][n % 10], tmps[t][n % 10]);
}
return 0;
}