1. 程式人生 > >[CF1073E]Segment Sum

[CF1073E]Segment Sum

題目大意:給定$K,L,R$,求$[L,R]$之間最多不包含超過$K$種數字的數的和。

題解:數位$DP$,令$f_{i,j}$為選到第$i$個數,已經用了的數字狀態為$j$,令$nxt$為當前條件的後面的數,$f_{i,j}=\sum\limits_{nxt}(d\times10^i+nxt)(d為當前這一位填的數)$,$f_{i,j}=d\times10^i\sum_{nxt}1+\sum_{nxt}nxt$

可以記錄$\sum nxt^0$和$\sum nxt^1$轉移即可

卡點:

 

C++ Code:

#include <cstdio>
const int mod = 998244353;
int tot, num[20];
long long k, l, r;
inline void up(long long &a, long long b) {if ((a += b) >= mod) a -= mod;}

struct node {
	long long cnt, sum;
} f[20][1 << 10];

long long pw[20];
node calc(int x, int lim, int S) {
	if (!x) return (node) {1, 0};
	if (!lim && ~f[x][S].cnt) return f[x][S];
	node F = (node) {0, 0};
	for (int i = lim ? num[x] : 9; ~i; i--) {
		int nxt;
		if (!S && !i) nxt = 0;
		else nxt = S | 1 << i;
		if (__builtin_popcount(nxt) > k) continue;
		node tmp = calc(x - 1, lim && i == num[x], nxt);
		up(F.cnt, tmp.cnt);
		up(F.sum, (tmp.sum + tmp.cnt * pw[x - 1] % mod * i) % mod);
	}
	if (!lim) f[x][S] = F;
	return F;
}
long long solve(long long x) {
	tot = 0;
	while (x) {
		num[++tot] = x % 10;
		x /= 10;
	}
	return calc(tot, 1, 0).sum;
}
int main() {
	__builtin_memset(f, -1, sizeof f);
	scanf("%lld%lld%lld", &l, &r, &k);
	pw[0] = 1; for (int i = 1; i < 20; i++) pw[i] = pw[i - 1] * 10 % mod;
	printf("%lld\n", (solve(r) - solve(l - 1) + mod) % mod);
	return 0;
}