[AHOI2009]self同類分佈

題目

傳送門 to BZOJ

傳送門 to VJ

傳送門 to luogu

思路

數位 dpdp 亂搞。考慮到各位數字之和的範圍很小,枚舉它,設爲 rr ,之後再做。

f(i,j)f(i,j) 表示各位數字之和目前爲 ii ,原數模 rr 的結果是 jj ,有多少個這樣的數字。

統計答案用 f(r,0)f(r,0) 即可。似乎狀態數有點多?

然後開始玄學優化。說實話,我討厭這種亂優化(單純的剪枝)的東西。出這種題真的很煩!

  • 可行性剪枝(無意義狀態):後方都是 99 也得不到 rr ,就再見;已經超過了 rr 也不行。
  • 可行性剪枝(無意義轉移):當前考慮了 xx 個數位,則只有 i9xi\le 9xf(i,j)f(i,j) 可能不爲零。

然後就過了。你說這種題是不是很煩?

代碼

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
inline int readint() {
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
void writeint(int x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}
# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }
# define FOR(i,n) for(int i=0; i<(n); ++i)
typedef long long int_;

int_ dp[163][162][2], tmp[163][162][2];

void cls(int x,int r,int_ a[163][162][2]){ // 清空數組
	for(int i=max(0,r-9*(19-x)); i<=9*x and i<=r; ++i)
		FOR(j,r) FOR(k,2) a[i][j][k] = 0;
}

int d[19];
int_ work(int_ num){
	for(int i=18; ~i; --i)
		d[i] = num%10, num /= 10;
	int_ res = 0;
	for(int r=1; r<163; ++r){ // 數字之和
		dp[0][0][0] = 1; // 常見初狀態
		for(int x=0; x<19; cls(x++,r,tmp)){
			FOR(now,10) FOR(j,r) // 非常窘迫的剪枝
				for(int i=max(0,r-9*(18-x)-now); i+now<=r and i<=9*x; ++i){
					int_ *p = tmp[i+now][(j*10+now)%r];
					if(now <= d[x])
						p[now < d[x]] += dp[i][j][0];
					p[1] += dp[i][j][1];
				}
			swap(dp,tmp); // 據說是O(1)的?
		}
		res += dp[r][0][1]; // 很穩!
		cls(19,r,dp); // 清空好習慣
	}
	return res;
}

int main(){
	int_ x, ans = 0;
	cin >> x, ans -= work(x);
	cin >> x, ans += work(x+1);
	cout << ans << endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章