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

  

posted @ 2018-11-05 19:23  Memory_of_winter  阅读(361)  评论(1编辑  收藏  举报