bzoj 2999 inint——数论——暑假篇

这道题太毒瘤

一.题目

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;
}

谢谢!

发布了61 篇原创文章 · 获赞 32 · 访问量 8347

猜你喜欢

转载自blog.csdn.net/weixin_43908980/article/details/97363575