[CF924F/CF956F]Minimal Subset Difference

题目

传送门 to CF

思路

要点:敢打就敢过

如果给出一个数字,直接判断,显然是背包问题。于是考虑将这个可行性背包的 01 01 01 值状压下来。要存储 2 × 9 × 9 + 1 2\times 9\times 9+1 2×9×9+1 的长度的容量,也就是 2 163 2^{163} 2163 个不同的情况。这我玩啥啊

但是,打表发现,实际上只有约 16 k 16\text k 16k 种可达到的状态。提前建好显式 d p \tt dp dp 状态转移图。为了解决多组询问,我们预处理 g k ( j , o , i ) g_k(j,o,i) gk(j,o,i) 表示剩余 j j j 个数位,第 1 1 1 个数位只能填 [ 0 , i ] [0,i] [0,i] 中数字、剩余随意,目前状态是 o o o,有多少个数字。转移是比较简单的,只是提醒一点: j j j 作为最外层循环,而不是 o o o,因为 状态转移有环(因为我们只保留了 163 163 163 位;相当于对应的生成函数在模 x 163 x^{163} x163 意义下成环,这是有可能发生的)。

复杂度 O ( k log ⁡ 10 r ⋅ X D ) \mathcal O(k\log_{10} r\cdot XD) O(klog10rXD),其中 X = 16116 X=16116 X=16116 是状态数, D = 10 D=10 D=10 是数位范围。所以问题来了:为什么 2 163 2^{163} 2163 里面只有 16116 16116 16116 个可行?

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <bitset>
#include <map>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
    
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int DPLEN = 163, MOD = 1e9+7;
int to_int(const bitset<DPLEN> &bs){
    
    
	int res = 0;
	for(int i=0; i!=DPLEN; ++i)
		res = ((res<<1)|bs.test(i))%MOD;
	return res; // keep int32_t
}

const int MAXN = 16116;
map<int,int> mp;
int trans[MAXN][10], tot;
llong g[10][20][MAXN][10]; ///< k, number of digits, node, bound of next digit
/// @return the index of the node of @p bs
int dfs(const bitset<DPLEN> &bs){
    
    
	const int v = to_int(bs);
	if(mp.count(v)) return mp[v];
	const int &id = mp[v] = ++ tot;
	trans[id][0] = id; // always self-loop
	for(int i=1; i!=10; ++i){
    
    
		bitset<DPLEN> nxt = (bs<<i)|(bs>>i);
		trans[id][i] = dfs(nxt);
	}
	int minK = 10;
	rep(i,0,9) if(bs.test((DPLEN>>1)+i)
		|| bs.test((DPLEN>>1)-i)){
    
    
			minK = i; break;
		}
	for(int k=minK; k!=10; ++k)
		rep(i,0,9) g[k][0][id][i] = 1;
	return id; // node index
}
void build(){
    
    
	bitset<DPLEN> now; now.reset();
	now.set(DPLEN>>1), tot = -1; dfs(now); // root = 0
	rep(k,0,9) rep(j,1,19) rep(o,0,tot) rep(i,0,9){
    
    
		g[k][j][o][i] = g[k][j-1][trans[o][i]][9];
		if(i) g[k][j][o][i] += g[k][j][o][i-1];
	}
}

int dig[20];
llong solve(int k,llong r){
    
    
	int top = 0, o = 0; llong ans = 0;
	for(; r; r/=10) dig[++ top] = r%10;
	for(int i=top; i; --i){
    
    
		if(dig[i]) ans += g[k][i][o][dig[i]-1];
		o = trans[o][dig[i]];
	}
	return ans;
}
int main(){
    
    
	build(); // OH MY GOD!!!
	// printf("%d\n",tot);
	for(int T=readint(); T; --T){
    
    
		llong l, r; scanf("%lld %lld",&l,&r);
		int k = readint();
		printf("%lld\n",solve(k,r+1)-solve(k,l));
	}
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_42101694/article/details/122407757