數位 dp / lianggj 讓你學小專題應該怎麼辦

ChElsYqwq發表於2024-04-02

P4999

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)

using namespace std;

const int N=20, M=9*18+5, P=1e9+7; 

int t, l, r, f[N][M], a[N], len, ans;

/*
Q:查詢 i in [l,r] 的 i 的數位和。
A:f[i][j] 表示任意填寫 pos=1,...,i 的,以前填了 j 的數和,這個時候的任意合法情況數位和 
*/ 
int dfs(int i,bool limit,int sum) {
	if(!i) return sum; // 邊界 
	if(!limit&&f[i][sum]>=0) return f[i][sum]; // 記憶化 
	int d=limit?a[i]:9, res=0; // 求一個上界 
	up(v,0,d) res=(res+dfs(i-1,limit&&v==a[i],sum+v))%P; // 列舉填什麼東西 
	if(!limit) f[i][sum]=res; // 記憶化 
	return res;
}

int solve(int x) {
	len=0; // 拆位喵 
	while(x) a[++len]=x%10, x/=10;
	return dfs(len,1,0);
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	memset(f,-1,sizeof(f));
	cin >> t;
	while(t--) {
		cin >> l >> r, ans=solve(r)-solve(l-1); // 容斥成 [1,x] 的詢問 
		cout << (ans%P+P)%P << '\n';
	}
	return 0;
}

P2602

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)

using namespace std;

const int N=20, M=9*18+5; 

int t, l, r, f[N][M][2], a[N], len, ans[10], digit; 
/*
Q: i in [l,r] 中每個數碼的出現次數,對於 digit=1,...,9 答案顯然相同 
A: f[i][j][0/1] 表示任意填 pos=1,...,i,以前填了 j 個這種數碼,這個數碼是/不是 0 的情況下的該數碼出現總次數 
*/ 

int dfs(int i,bool limit,bool lead,int cnt) {
	// 含義是填了 i+1,...,len 現在要填 i,能不能任意填寫,前導 0 有沒有被消除,現在填了 cnt 個數碼的數碼出現總次數 
	if(!i) return cnt;
	auto &now=f[i][cnt][digit!=0];
	if(!limit&&!lead&&now>=0) return now;
	int d=limit?a[i]:9, res=0;
	up(v,0,d) {
		int val=(lead&&digit==0&&v==0)?0:(cnt+(v==digit)); // 前導 0 不能算進去 >w< 
		res+=dfs(i-1,limit&&v==a[i],lead&(v==0),val); 
	}
	if(!limit&&!lead) now=res;
	return res;
}

void solve(int x,int k) {
	len=0;
	while(x) a[++len]=x%10, x/=10;
	up(i,0,9) digit=i, ans[i]+=k*dfs(len,1,1,0);
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	memset(f,-1,sizeof(f));
	cin >> l >> r;
	solve(l-1,-1), solve(r,1);
	up(i,0,9) cout << ans[i] << ' ';
	return 0;
}

相關文章