這道題太毒瘤
一.題目
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;
}