6831. 2020.10.24【NOIP提高A组】T1.lover

表了一下,发现\(dig\)有值的数大概只有几万个,可以考虑做做。
然后发现它要\(gcd<=K\),考虑分解质因数后\(DP\)。然后不会。

考虑\(DP\),显然对答案有贡献的数位可以拆分成质因子。
\(f[x][0/1][a][b][c][d]\)表示当前到了第\(x\)位,是否有顶格,\(gcd\)\(2^a*3^b*5^c*7^d\)的方案数。
显然可以用数位\(DP\)转移,转移不讲。

\(g[a][b][c][d]\)表示\(f[0][a][b][c][d]+f[1][a][b][c][d]\)
我们发现可以通过高维前缀和然后容斥来解决问题。
\(g\)为后缀和后的\(g\),设\(h\)为两两间\(gcd\)\(2^a*3^b*5^c*7^d\)的方案数。\(Solution\ 1\)\(h[a][b][c][d]\)显然可以用容斥来解决。\(h[a][b][c][d]=g[a][b][c][d]^2-g[a+1][b][c][d]^2-g[a][b+1][c][d]^2-g[a][b][c+1][d]^2-g[a][b][c][d+1]^2+......\)
当然这种写法十分的麻烦(吧)

\(Solution\ 2\)
高维前缀和以后,设\(p[a][b][c][d]\)表示\(h\)的后缀和
那么可以惊奇地发现:\(p\)=\(g*g\)!!!
是的,就是这么的神奇,求出\(p\)以后在逆推回去就得到\(h\)了。
答案就是把所有\(<=gcd\)的方案数加起来。。。

\(Code(Solution\ 2)\)

这数位\(DP\)和高维前缀和真是累死人不偿命。。。

#include <cstdio>
#include <cstring>
#define mo 998244353
#define db double
#define ll long long
#define mem(x, a) memset(x, a, sizeof x)
#define mpy(x, y) memcpy(x, y, sizeof y)
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
#define go(x) for (int p = tail[x], v; p; p = e[p].fr)
#define plus(x, y) x = x + y >= mo ? x + y - mo : x + y
#define nplus(x, y) x = x + mo - y >= mo ? x - y : x + mo - y
using namespace std;
const int fen[10][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}, 
						{1, 0, 0, 0}, {0, 1, 0, 0},
						{2, 0, 0, 0}, {0, 0, 1, 0},
						{1, 1, 0, 0}, {0, 0, 0, 1},
						{3, 0, 0, 0}, {0, 2, 0, 0}};
int f[61][39][27][23], g[61][39][27][23], len, ans = 0;
int las_[4];
ll n, K, cf[19], cf0[4][60];
bool nonono = 0;

int get_len(ll x) {
	int s = 0; while (x) s++, x /= 10;
	cf[0] = 1; fo(i, 1, s) cf[i] = cf[i - 1] * 10;
	return s;
}


int main()
{
	freopen("lover.in", "r", stdin);
	freopen("lover.out", "w", stdout);
	scanf("%lld%lld", &n, &K);
	len = get_len(n);
	
	int fir_ = n / cf[len - 1] % 10;
	fo(i, 1, fir_ - 1) f[fen[i][0]][fen[i][1]][fen[i][2]][fen[i][3]] = 1;
	fo(i, 0, 3) las_[i] = fen[fir_][i];
	
	cf0[0][0] = cf0[1][0] = cf0[2][0] = cf0[3][0] = 1;
	fo(i, 1, 59) cf0[0][i] = cf0[0][i - 1] << 1;
	fo(i, 1, 37) cf0[1][i] = cf0[1][i - 1] * 3;
	fo(i, 1, 25) cf0[2][i] = cf0[2][i - 1] * 5;
	fo(i, 1, 21) cf0[3][i] = cf0[3][i - 1] * 7;
	
	fo(i, 2, len) {
		int val = n / cf[len - i] % 10;
		if (val == 0) nonono = 1;
		mpy(g, f); mem(f, 0);
		fo(j, 0, 59) {
			if (cf0[0][j] > cf[i - 1]) break;
			fo(k, 0, 37) {
			if (cf0[0][j] * cf0[1][k] > cf[i - 1]) break;
			fo(l, 0, 25) {
			if (cf0[0][j] * cf0[1][k] * cf0[2][l] > cf[i - 1]) break;
			fo(p, 0, 21) {
			if (cf0[0][j] * cf0[1][k] * cf0[2][l] * cf0[3][p] > cf[i - 1]) break;
			if (! g[j][k][l][p]) continue;
			fo(q, 1, 9) {
				if (j + fen[q][0] > 59 || k + fen[q][1] > 37 || l + fen[q][2] > 25 || p + fen[q][3] > 21) continue;
				plus(f[j + fen[q][0]][k + fen[q][1]][l + fen[q][2]][p + fen[q][3]], g[j][k][l][p]);
			}
			}
			}
			}
		}
		
		fo(q, 1, 9) plus(f[fen[q][0]][fen[q][1]][fen[q][2]][fen[q][3]], 1);
		if (nonono == 0) {
			fo(q, 1, val - 1)
				plus(f[las_[0] + fen[q][0]][las_[1] + fen[q][1]][las_[2] + fen[q][2]][las_[3] + fen[q][3]], 1);
		}
		fo(j, 0, 3) las_[j] += fen[val][j];
	}
	if (nonono == 0) f[las_[0]][las_[1]][las_[2]][las_[3]]++;
	/*
	fo(j, 0, 59) fo(k, 0, 37) fo(l, 0, 25) fo(p, 0, 21) {
		if (! f[j][k][l][p]) continue;
		printf("%d %d %d %d, %lld\n", j, k, l, p, cf0[0][j] * cf0[1][k] * cf0[2][l] * cf0[3][p]);
		printf("%d\n", f[j][k][l][p]);
	}
	return 0;
	*/
	fd(j, 59, 0) fd(k, 37, 0) fd(l, 25, 0) fd(p, 21, 0)
		plus(f[j][k][l][p], f[j + 1][k][l][p]);
	fd(j, 59, 0) fd(k, 37, 0) fd(l, 25, 0) fd(p, 21, 0)
		plus(f[j][k][l][p], f[j][k + 1][l][p]);
	fd(j, 59, 0) fd(k, 37, 0) fd(l, 25, 0) fd(p, 21, 0)
		plus(f[j][k][l][p], f[j][k][l + 1][p]);
	fd(j, 59, 0) fd(k, 37, 0) fd(l, 25, 0) fd(p, 21, 0)
		plus(f[j][k][l][p], f[j][k][l][p + 1]);
	fo(j, 0, 59) fo(k, 0, 37) fo(l, 0, 25) fo(p, 0, 21)
		g[j][k][l][p] = (ll)f[j][k][l][p] * f[j][k][l][p] % mo;
	fo(j, 0, 59) fo(k, 0, 37) fo(l, 0, 25) fo(p, 0, 21)
		nplus(g[j][k][l][p], g[j + 1][k][l][p]);
	fo(j, 0, 59) fo(k, 0, 37) fo(l, 0, 25) fo(p, 0, 21)
		nplus(g[j][k][l][p], g[j][k + 1][l][p]);
	fo(j, 0, 59) fo(k, 0, 37) fo(l, 0, 25) fo(p, 0, 21)
		nplus(g[j][k][l][p], g[j][k][l + 1][p]);
	fo(j, 0, 59) fo(k, 0, 37) fo(l, 0, 25) fo(p, 0, 21)
		nplus(g[j][k][l][p], g[j][k][l][p + 1]);
		
	fo(j, 0, 59) {
		if (cf0[0][j] > K) break;
		fo(k, 0, 37) {
		if (cf0[0][j] * cf0[1][k] > K) break;
		fo(l, 0, 25) {
		if (cf0[0][j] * cf0[1][k] * cf0[2][l] > K) break;
		fo(p, 0, 21) {
			if (cf0[0][j] * cf0[1][k] * cf0[2][l] * cf0[3][p] > K) break;
			plus(ans, g[j][k][l][p]);
		}
	}
	}
	}
	printf("%d\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值