1. 程式人生 > >[BZOJ1799] [AHOI2009] 同類分佈 [數位dp]

[BZOJ1799] [AHOI2009] 同類分佈 [數位dp]

[ L i n k \frak{Link} ]


[a,b]滿足f(i)的數字個數;f(i)與數位相關。考慮數位dp
簡單想法是傳當前確定的數位和、還有當前確定的那一部分原數就好了。
然而第一個就算了,第二個根本放不下(不放數組裡面的話只靠第一個確定不了狀態)

回到問題來。只是“整除”可能有點模糊?
[a,b]中,被自身各數位和整除的數字個數  \Rightarrow   C ( {

   x       x    m o d   
x i    =    0    } ) \frak{C(\{\;x\;|\;x\;mod\;\sum x_i\;=\;0\;\})}

這麼考慮呢?
發現什麼了嗎?對了,mod
考慮把相對較小的餘數和模數儲存進來,這樣的話狀態是:
f(pos,sum,mod,remainder)
其中pos≤18, sum≤162, mod≤162, remainder<162……是不是有點大?

好像這個爆了空間。時間勉勉強強吧,在能過和不能過中間徘徊。
好像font就是mod,能不能合併哇?
這樣的話就變成了算到pos位,%mod=remainder。要求最後sum=mod
而且這樣正好能夠保證原數被狀態唯一確定。
注意這樣的話有一個mod沒有被記錄進去,然而mod不同,f也不同。所以每次memset
實際上這樣並沒有在時間上合併summod,不過至少空間能過了。時間就卡一卡吧。
記憶化搜尋的關係,多剪一下枝的話跑起來估計還勉強吧?
就是不知道kpm是咋整的能卡到1000ms


Accepted 5272ms
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
using namespace std;
long long a,b;
int bit[20];
long long F[20][165][165];
long long dfs(const int &pos, const int &sum, const int &mod, const int &remainder, const bool &limit) {
	if(sum - pos * 9 > 0) return 0;
	if(!pos && !sum) return !remainder;
	if(!limit && F[pos][sum][remainder] != -1) return F[pos][sum][remainder];
	int up = limit ? bit[pos] : 9;
	if(sum < up) up = sum;
	long long ret = 0;
	for(int t, i = 0; i <= up; ++i) {
		ret += dfs(pos - 1, sum - i, mod, (remainder * 10 + i) % mod, limit && i == up);
	}
	if(!limit) F[pos][sum][remainder] = ret;
	return ret;
}
long long solve(long long &x) {
	bit[0] = 0;
	while(x) {
		bit[++bit[0]] = int(x % 10);
		x /= 10;
	}
	long long ret = 0;
	for(int i = 1; i <= 9 * bit[0]; ++i) {
		memset(F, -1, sizeof(F));
		ret += dfs(bit[0], i, i, 0, true);
	}
	return ret;
}
int main() {
	scanf("%lld%lld", &a, &b);
	printf("%lld", solve(b) - solve(--a));
	return 0;
}