强烈推荐记忆化搜索写法,好写,通用。
入门题:
hdoj 2089
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); int table[10][2]; int digit[10]; int dfs(int len, int flag, bool bound) { if(len == 0) return 1; if(!bound && table[len][flag] != -1) return table[len][flag]; int up = bound? digit[len]: 9; int ret = 0; for(int i = 0; i <= up; ++i) { if(i == 4 || (flag && i == 2)) continue; ret += dfs(len-1, (i == 6? 1: 0), bound && i == up); } if(!bound) table[len][flag] = ret; return ret; } int fun(int num) { int len = 1; while(true) { digit[len] = num%10; num /= 10; if(num == 0) break; ++len; } return dfs(len, 0, true); } int main() { memset(table, -1, sizeof(table)); int a, b; while(scanf("%d%d", &a, &b), a+b) printf("%d\n", fun(b)-fun(a-1)); return 0; }
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); LL table[25][2][2]; int digit[25]; LL dfs(int len, int flag1, int flag2, bool bound) { if(len == 0) return flag2? 1: 0; if(!bound && table[len][flag1][flag2] != -1) return table[len][flag1][flag2]; int up = bound? digit[len]: 9; LL ret = 0; for(int i = 0; i <= up; ++i) ret += dfs(len-1, (i == 4? 1: 0), flag2|(flag1 && i == 9? 1: 0), bound && i == up); if(!bound) table[len][flag1][flag2] = ret; return ret; } LL fun(LL num) { int len = 1; while(true) { digit[len] = num%10; num /= 10; if(num == 0) break; ++len; } return dfs(len, 0, 0, true); } int main() { memset(table, -1, sizeof(table)); int TC; scanf("%d", &TC); while(TC--) { LL n; scanf("%I64d", &n); printf("%I64d\n", fun(n)); } return 0; }
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); int table[15][10]; int digit[15]; int dfs(int len, int pre, bool bound, bool zero) { if(len == 0) return 1; if(!bound && !zero && table[len][pre] != -1) return table[len][pre]; int up = bound? digit[len]: 9; int ret = 0; for(int i = 0; i <= up; ++i) { if(!zero && abs(i-pre) < 2) continue; ret += dfs(len-1, i, bound && i == up, zero && i == 0); } if(!bound && !zero) table[len][pre] = ret; return ret; } int fun(int num) { int len = 1; while(true) { digit[len] = num%10; num /= 10; if(num == 0) break; ++len; } return dfs(len, 0, true, true); } int main() { memset(table, -1, sizeof(table)); int a, b; while(~scanf("%d%d", &a, &b)) printf("%d\n", fun(b)-fun(a-1)); return 0; }
常见题型:
hdoj 3652
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); int table[15][13][2][2]; int digit[15]; int dfs(int len, int divide, int flag1, int flag2, bool bound) { if(len == 0) return (divide == 0 && flag2? 1: 0); if(!bound && table[len][divide][flag1][flag2] != -1) return table[len][divide][flag1][flag2]; int up = bound? digit[len]: 9; int ret = 0; for(int i = 0; i <= up; ++i) ret += dfs(len-1, (divide*10+i)%13, (i == 1? 1: 0), flag2|(i == 3 && flag1? 1: 0), bound && i == up); if(!bound) table[len][divide][flag1][flag2] = ret; return ret; } int fun(int num) { int len = 1; while(true) { digit[len] = num%10; num /= 10; if(num == 0) break; ++len; } return dfs(len, 0, 0, 0, true); } int main() { memset(table, -1, sizeof(table)); int n; while(~scanf("%d", &n)) printf("%d\n", fun(n)); return 0; }
这个题我是枚举的fix位,因为一个balance number不可能有俩个fix位,所以满足加法原理,不知道有没有更好的办法
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); LL table[20][20][1800]; int digit[20]; LL dfs(int fix, int len, int presum, bool bound, bool zero) { if(len == 0) return presum == 0? 1: 0; if(!bound && !zero && table[fix][len][presum] != -1) return table[fix][len][presum]; int up = bound? digit[len]: 9; LL ret = 0; for(int i = 0; i <= up; ++i) { LL temp = presum+(len-fix)*i; if((zero && i == 0 && fix == len) || temp < 0) continue; ret += dfs(fix, len-1, temp, bound && i == up, zero && i == 0); } if(!bound && !zero) table[fix][len][presum] = ret; return ret; } LL fun(LL num) { int len = 1; while(true) { digit[len] = num%10; num /= 10; if(num == 0) break; ++len; } LL ret = 0; for(int i = len; i >= 1; --i) ret += dfs(i, len, 0, true, true); return ret+1; } int main() { memset(table, -1, sizeof(table)); int TC; scanf("%d", &TC); while(TC--) { LL a, b; scanf("%I64d%I64d", &a, &b); printf("%I64d\n", fun(b)-(a? fun(a-1): 0)); } return 0; }
数位DP+构造或者二分都可以
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); int table[40][500]; int digit[40]; int BASE, M; int dfs(int len, int sum, bool bound) { if(len == 0) return sum == M? 1: 0; if(!bound && table[len][sum] != -1) return table[len][sum]; int up = bound? digit[len]: BASE-1; int ret = 0; for(int i = 0; i <= up; ++i) { int temp = sum+i; if(temp > M) continue; ret += dfs(len-1, temp, bound && i == up); } if(!bound) table[len][sum] = ret; return ret; } int fun(int num) { int len = 1; while(true) { digit[len] = num%BASE; num /= BASE; if(num == 0) break; ++len; } return dfs(len, 0, true); } int main() { int Q, X, Y, K, n_case(0); while(~scanf("%d", &Q)) { printf("Case %d:\n", ++n_case); if(Q == 1) { memset(table, -1, sizeof(table)); scanf("%d%d%d%d", &X, &Y, &BASE, &M); if(X > Y) swap(X, Y); printf("%d\n", fun(Y)-(X? fun(X-1): 0)); } else { memset(table, -1, sizeof(table)); scanf("%d%d%d%d%d", &X, &Y, &BASE, &M, &K); if(X > Y) swap(X, Y); int l = X, r = Y+1; int limit = X? fun(X-1): 0; while(l < r) { int m = l+(r-l)/2; if(fun(m)-limit < K) l = m+1; else r = m; } if(l == Y+1) printf("Could not find the Number!\n"); else printf("%d\n", l); } } return 0; }
枚举拆分位,由于这题算的是拆分种数,所以直接加和即可
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); LL table[20][25]; LL pow10[20]; int digit[20]; int K, sep; LL dfs(int len, LL divide, bool bound, bool zero) { if(len == 0) return divide? 0: 1; if(!bound && !zero && table[len][divide] != -1) return table[len][divide]; int up = bound? digit[len]: 9; LL ret = 0; for(int i = 0; i <= up; ++i) { if(zero && i == 0 && len == sep+1) continue; ret += dfs(len-1, (divide+(len > sep? i*pow10[len-sep-1]: i*pow10[len-1]))%K, bound && i == up, zero && i == 0); } if(!bound && !zero) table[len][divide] = ret; return ret; } LL fun(LL num) { int len = 1; while(true) { digit[len] = num%10; num /= 10; if(num == 0) break; ++len; } LL ret = 0; for(int i = len-1; i >= 1; --i) { sep = i; memset(table, -1, sizeof(table)); ret += dfs(len, 0, true, true); } return ret; } int main() { pow10[0] = 1; for(int i = 1; i < 20; ++i) pow10[i] = pow10[i-1]*10; LL A, B; while(~scanf("%I64d%I64d%d", &A, &B, &K)) printf("%I64d\n", fun(B)-(A? fun(A-1): 0)); return 0; }
HDOJ 3565
求由两个单峰数组成的数字中数位和最大的最大值,注意单峰数的定义,这题不满足区间减法,所以要同时要卡上界和下界(其实这种写法更加通用),state是上一位所在的状态,共有7种,0表示前导0,1表示第一个上升坡,2表示第一个顶点,3表示第一个下降坡,4表示第二个上升坡,5表示第二个顶点,6表示第二个下降坡.
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); bool vis[25][8][10]; int table[25][8][10]; int digitlow[25], digitup[25]; int dfs(int len, int state, int pre, bool lowbound, bool upbound) { if(state == 0 && len < 6) return -1; if(len == 0) return state == 5? 0: -1; if(!lowbound && !upbound && vis[len][state][pre]) return table[len][state][pre]; int low = lowbound? digitlow[len] : 0, up = upbound? digitup[len]: 9; int ret = -1; for(int i = low; i <= up; ++i) { int temp; switch(state) { case 0: temp = dfs(len-1, (i == 0? 0: 1), i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); break; case 1: if(i > pre) { temp = dfs(len-1, 1, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); temp = dfs(len-1, 2, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); } break; case 2: if(i < pre) { temp = dfs(len-1, 3, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); } break; case 3: if(i < pre) { temp = dfs(len-1, 3, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); } if(i != 0) { temp = dfs(len-1, 4, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); } break; case 4: if(i > pre) { temp = dfs(len-1, 4, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); temp = dfs(len-1, 5, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); } break; case 5: if(i < pre) { temp = dfs(len-1, 5, i, lowbound && i == low, upbound && i == up); if(temp != -1) ret = max(ret, temp+i); } break; } } if(!lowbound && !upbound) { vis[len][state][pre] = true; table[len][state][pre] = ret; } return ret; } int fun(ULL numlow, ULL numup) { int len1, len2; len1 = 1; while(true) { digitlow[len1] = numlow%10; numlow /= 10; if(numlow == 0) break; ++len1; } len2 = 1; while(true) { digitup[len2] = numup%10; numup /= 10; if(numup == 0) break; ++len2; } for(int i = len1+1; i <= len2; ++i) digitlow[i] = 0; return dfs(len2, 0, 0, true, true); } int main() { int TC, n_case(0); scanf("%d", &TC); while(TC--) { ULL n1, n2; scanf("%I64u%I64u", &n1, &n2); printf("Case %d: ", ++n_case); int ans = fun(n1, n2); printf("%d\n", ans == -1? 0: ans); } return 0; }
hdoj 3943
数位DP+二分或构造
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); bool vis[25][22][22]; ULL table[25][22][22]; int digit[25]; int X, Y; ULL dfs(int len, int qua1, int qua2, bool bound) { if(len == 0) return X == qua1 && Y == qua2? 1: 0; if(!bound && vis[len][qua1][qua2]) return table[len][qua1][qua2]; int up = bound? digit[len]: 9; ULL ret = 0; for(int i = 0; i <= up; ++i) { int t1 = qua1+(i == 4? 1: 0), t2 = qua2+(i == 7? 1: 0); if(t1 > X || t2 > Y) continue; ret += dfs(len-1, t1, t2, bound && i == up); } if(!bound) { vis[len][qua1][qua2] = true; table[len][qua1][qua2] = ret; } return ret; } ULL fun(ULL num) { int len = 1; while(true) { digit[len] = num%10; num /= 10; if(num == 0) break; ++len; } return dfs(len, 0, 0, true); } int main() { int TC, n_case(0); scanf("%d", &TC); while(TC--) { ULL P, Q; scanf("%I64u%I64u%d%d", &P, &Q, &X, &Y); memset(vis, 0, sizeof(vis)); ULL limit = fun(P); int n; scanf("%d", &n); printf("Case #%d:\n", ++n_case); for(int i = 0; i < n; ++i) { ULL l = P+1, r = Q+1, K; scanf("%I64u", &K); while(l < r) { ULL m = l+(r-l)/2; if(fun(m)-limit < K) l = m+1; else r = m; } if(l == Q+1) printf("Nya!\n"); else printf("%I64u\n", l); } } return 0; }
hdoj 4389
table[len][i][j][k]表示剩余len位,f(x) = i,已经生成的前缀位%f(x)=j,生成的前缀位的数位和为k最后可以生成的合法数字个数.
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); int table[15][82][82][82]; int reg; int digit1[15], digit2[15]; int dfs(int len, int divide, int presum, bool lowbound, bool upbound) { if(len == 0) return divide == 0 && presum == reg? 1: 0; if(!upbound && !lowbound && table[len][reg][divide][presum] != -1) return table[len][reg][divide][presum]; int up = upbound? digit2[len]: 9, low = lowbound? digit1[len]: 0; int ret = 0; for(int i = low; i <= up; ++i) ret += dfs(len-1, (divide*10+i)%reg, presum+i, lowbound && i == low, upbound && i == up); if(!upbound && !lowbound) table[len][reg][divide][presum] = ret; return ret; } int fun(int num1, int num2) { int len1 = 1; while(true) { digit1[len1] = num1%10; num1 /= 10; if(num1 == 0) break; ++len1; } int len2 = 1; while(true) { digit2[len2] = num2%10; num2 /= 10; if(num2 == 0) break; ++len2; } for(int i = len1+1; i <= len2; ++i) digit1[i] = 0; int ret = 0; for(int i = 1; i <= 81; ++i) { reg = i; ret += dfs(len2, 0, 0, true, true); } return ret; } int main() { memset(table, -1, sizeof(table)); int TC, n_case(0); scanf("%d", &TC); while(TC--) { int a, b; scanf("%d%d", &a, &b); printf("Case %d: %d\n", ++n_case, fun(a, b)); } return 0; }
Codeforces 55D. Beautiful numbers
很明显的数位DP,但状态不好设计,开始想了一种很暴力的状态
table[len][state][r9][r8][r7][r6][r5][r4][r3][r2],表示剩余len位时,(1,2,...9)访问状态为state,已生成的前缀位%i = ri,最后可以得到的合法数字个数,但这复杂度O(18*256*9*8*7*6*5*4*3*2*9)太可观了。。。
后来发现状态r6,r4,r3,r2不是必要的,最后可以由其他状态推出来,可以省略掉,于是复杂度变成了O(18*256*9*8*7*5*9),还是略高,写好后交上去试了一下,3秒卡过
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(1000000007); const ULL LIM(1000000000000000ull); LL table[19][1 << 8][9][8][7][5]; int digit1[20], digit2[20]; LL dfs(int len, int state, int rem9, int rem8, int rem7, int rem5, bool lowbound, bool upbound) { if(len == 0) { if((state&(1 << 7)) && rem9) return 0; if((state&(1 << 6)) && rem8) return 0; if((state&(1 << 5)) && rem7) return 0; if((state&(1 << 4)) && (rem9%3 || rem8%2)) return 0; if((state&(1 << 3)) && rem5) return 0; if((state&(1 << 2)) && rem8%4) return 0; if((state&(1 << 1)) && rem9%3) return 0; if((state&(1 << 0)) && rem8%2) return 0; return 1; } if(!lowbound && !upbound && table[len][state][rem9][rem8][rem7][rem5] != -1) return table[len][state][rem9][rem8][rem7][rem5]; int low = lowbound? digit1[len]: 0, up = upbound? digit2[len]: 9; LL ret = 0; for(int i = low; i <= up; ++i) ret += dfs(len-1, (i >= 2? state|(1 << (i-2)): state), (rem9*10+i)%9, (rem8*10+i)%8, (rem7*10+i)%7, (rem5*10+i)%5, lowbound && i == low, upbound && i == up); if(!lowbound && !upbound) table[len][state][rem9][rem8][rem7][rem5] = ret; return ret; } LL fun(LL num1, LL num2) { int len1 = 1; while(true) { digit1[len1] = num1%10; num1 /= 10; if(num1 == 0) break; ++len1; } int len2 = 1; while(true) { digit2[len2] = num2%10; num2 /= 10; if(num2 == 0) break; ++len2; } for(int i = len1+1; i <= len2; ++i) digit1[i] = 0; return dfs(len2, 0, 0, 0, 0, 0, true, true); } int main() { memset(table, -1, sizeof(table)); int TC; scanf("%d", &TC); while(TC--) { LL a, b; scanf("%I64d%I64d", &a, &b); printf("%I64d\n", fun(a, b)); } return 0; }
然后就是看题解了,其中应用了俩个性质
1 a = k*b, 则 (n%a)%b = n%b
2 如果 n如果能被 any(a1, a2, ...ak)整除,则有n能被lcm(a1, a2, ...ak)整除
因为lcm(2, 3, 4,...9) = 2520,所以应用这俩个性质就可以设计出一个不错的状态了
table[len][lc][k]表示剩余len位,前缀数位的lcm为lc,生成的前缀%2520为k最后可以得到的合法数字个数
看上去复杂度O(18*2520*2520*9)还是很高,但实际上由于2520的约数个数只有49个,所以lc可以的取值只有49个
这样复杂度就变成了O(18*49*2520*9),空间也可以用离散化压缩,嗯,这样就可以接受了。
题解上还给了一种更加优化的方法,但还不是很理解(果然数学是硬伤%>_<%)。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(10); const int SIGMA_SIZE(2); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(2520); const ULL LIM(1000000000000000ull); LL table[20][50][2521]; int digit1[20], digit2[20]; int h[2521]; LL gcd(LL a, LL b) { LL temp; while(b) { temp = a%b; a = b; b = temp; } return a; } LL lcm(LL a, LL b) { return a/gcd(a, b)*b; } LL dfs(int len, int lc, int rem, bool lowbound, bool upbound) { if(len == 0) return rem%lc? 0: 1; if(!lowbound && !upbound && table[len][h[lc]][rem] != -1) return table[len][h[lc]][rem]; int low = lowbound? digit1[len]: 0, up = upbound? digit2[len]: 9; LL ret = 0; for(int i = low; i <= up; ++i) ret += dfs(len-1, i == 0? lc: lcm(lc, i), (rem*10+i)%MOD, lowbound && i == low, upbound && i == up); if(!lowbound && !upbound) table[len][h[lc]][rem] = ret; return ret; } LL fun(LL num1, LL num2) { int len1 = 1; while(true) { digit1[len1] = num1%10; num1 /= 10; if(num1 == 0) break; ++len1; } int len2 = 1; while(true) { digit2[len2] = num2%10; num2 /= 10; if(num2 == 0) break; ++len2; } for(int i = len1+1; i <= len2; ++i) digit1[i] = 0; return dfs(len2, 1, 0, true, true); } int main() { int count = 0; for(int i = 1; i <= 2520; ++i) if(2520%i == 0) h[i] = count++; memset(table, -1, sizeof(table)); int TC; scanf("%d", &TC); while(TC--) { LL a, b; scanf("%I64d%I64d", &a, &b); printf("%I64d\n", fun(a, b)); } return 0; }
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(1410); const int SIGMA_SIZE(10); const int MAXM(110); const int MAXE(300010); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const int MOD(2520); const ULL LIM(1000000000000000ull); struct AC { int ch[MAXN][SIGMA_SIZE], f[MAXN]; int val[MAXN]; int size; inline int idx(char temp) { return temp-'0'; } void init() { memset(ch[0], 0, sizeof(ch[0])); val[0] = 0; size = 1; } void insert(char *S) { int u = 0, id; for(; *S; ++S) { id = idx(*S); if(!ch[u][id]) { memset(ch[size], 0, sizeof(ch[size])); val[size] = false; ch[u][id] = size++; } u = ch[u][id]; } val[u] = 1; } int que[MAXN]; int front, back; void construct() { int cur, u; front = back = 0; for(int i = 0; i < SIGMA_SIZE; ++i) if(ch[0][i]) { u = ch[0][i]; f[u] = 0; que[back++] = u; } while(front < back) { cur = que[front++]; for(int i = 0; i < SIGMA_SIZE; ++i) { u = ch[cur][i]; if(u) { f[u] = ch[f[cur]][i]; val[u] |= val[f[u]]; que[back++] = u; } else ch[cur][i] = ch[f[cur]][i]; } } } }; AC ac; LL table[20][MAXN][2]; int digit1[20], digit2[20]; LL dfs(int len, int u, int flag, bool lowbound, bool upbound, bool zero) { if(len == 0) return flag? 1: 0; if(!lowbound && !upbound && !zero && table[len][u][flag] != -1) return table[len][u][flag]; int low = lowbound? digit1[len]: 0, up = upbound? digit2[len]: 9; LL ret = 0; for(int i = low; i <= up; ++i) if(zero && i == 0 && len > 1) ret += dfs(len-1, 0, flag, lowbound && i == low, upbound && i == up, true); else ret += dfs(len-1, ac.ch[u][i], flag|(ac.val[ac.ch[u][i]]), lowbound && i == low, upbound && i == up, zero && i == 0); if(!lowbound && !upbound && !zero) table[len][u][flag] = ret; return ret; } LL fun(LL num1, LL num2) { int len1 = 1; while(true) { digit1[len1] = num1%10; num1 /= 10; if(num1 == 0) break; ++len1; } int len2 = 1; while(true) { digit2[len2] = num2%10; num2 /= 10; if(num2 == 0) break; ++len2; } for(int i = len1+1; i <= len2; ++i) digit1[i] = 0; return dfs(len2, 0, 0, true, true, true); } char str[20]; int main() { LL a, b, K; int n; while(cin >> a >> b >> K >> n) { ac.init(); for(int i = 0; i < n; ++i) { cin >> str; ac.insert(str); } ac.construct(); memset(table, -1, sizeof(table)); LL l = a, r = b+1; while(l < r) { LL m = l+(r-l)/2; if(fun(a, m) < K) l = m+1; else r = m; } if(l == b+1) cout << "no such number\n"; else cout << l << "\n"; } return 0; }
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::istringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(200010); const int MAXM(10010); const int MAXE(10010); const int MAXH(19); const int INFI(2000000000); const int MOD(1000000007); const ULL BASE(31); const LL LIM(10000000); const int INV(-10000); LL table[33][33][2]; int digit1[33], digit2[33]; LL dfs(int len, int presum, int flag, bool lowbound, bool upbound) { if(len == 0) return presum; if(!lowbound && !upbound && table[len][presum][flag] != -1) return table[len][presum][flag]; int low = lowbound? digit1[len]: 0, up = upbound? digit2[len]: 1; LL ret = 0; for(int i = low; i <= up; ++i) ret += dfs(len-1, presum+(i&flag), i, lowbound && i == low, upbound && i == up); if(!lowbound && !upbound) table[len][presum][flag] = ret; return ret; } LL fun(int num1, int num2) { int len1 = 1; while(true) { digit1[len1] = num1&1; num1 >>= 1; if(num1 == 0) break; ++len1; } int len2 = 1; while(true) { digit2[len2] = num2&1; num2 >>= 1; if(num2 == 0) break; ++len2; } for(int i = len1+1; i <= len2; ++i) digit1[i] = 0; return dfs(len2, 0, 0, true, true); } int main() { memset(table, -1, sizeof(table)); int TC, n_case(0); scanf("%d", &TC); while(TC--) { int n; scanf("%d", &n); printf("Case %d: %lld\n", ++n_case, fun(0, n)); } return 0; }
BALNUM spoj10606
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::stringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PAIR; typedef multimap<int, int> MMAP; const int MAXN(1510); const int MAXM(5010); const int MAXE(10010); const int HSIZE(13131); const int SIGMA_SIZE(26); const int MAXH(19); const int INFI((INT_MAX-1) >> 1); const ULL BASE(31); const LL LIM(10000000); const int INV(-10000); int hash[1024][1024]; LL table[21][60010]; int lowdig[21], updig[21]; LL dfs(int len, int state1, int state2, bool zero, bool lowbound, bool upbound) { if(len == 0) { for(int i = 0; i <= 9; ++i) if((state1&(1 << i)) && ((state2&(1 << i))^((i&1) << i)) == 0) return 0; return 1; } if(!zero && !lowbound && !upbound && table[len][hash[state1][state2]] != -1) return table[len][hash[state1][state2]]; int low = lowbound? lowdig[len]: 0; int up = upbound? updig[len]: 9; LL temp = 0; for(int i = low; i <= up; ++i) if(zero && i == 0) temp += dfs(len-1, state1, state2, true, lowbound && i == low, upbound && i == up); else temp += dfs(len-1, state1|(1 << i), state2^(1 << i), false, lowbound && i == low, upbound && i == up); if(!zero && !lowbound && !upbound) table[len][hash[state1][state2]] = temp; return temp; } LL fun(ULL l, ULL u) { int len1 = 1; while(true) { lowdig[len1] = l%10; l /= 10; if(!l) break; ++len1; } int len2 = 1; while(true) { updig[len2] = u%10; u /= 10; if(!u) break; ++len2; } for(int i = len1+1; i <= len2; ++i) lowdig[i] = 0; return dfs(len2, 0, 0, true, true, true); } int main() { int cnt = 0; for(int i = 0; i < 1024; ++i) for(int j = 0; j < 1024; ++j) { bool flag(true); for(int k = 0; k <= 9; ++k) if(!(i&(1 << k)) && (j&(1 << k))) { flag = false; break; } if(flag) hash[i][j] = cnt++; } memset(table, -1, sizeof(table)); int TC; scanf("%d", &TC); ULL a, b; while(TC--) { scanf("%llu%llu", &a, &b); printf("%lld\n", fun(a, b)); } return 0; }
俩个回文数字题:
需要注意的是回文定义的状态还有无后效性(只对数字的前一半进行DP)
poj2402
http://poj.org/problem?id=2402
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> #include <bitset> #include <iomanip> //#pragma comment(linker, "/STACK:102400000,102400000") using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::stringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; using std::unique; using std::lower_bound; using std::random_shuffle; using std::bitset; using std::upper_bound; using std::multiset; using std::ios; using std::make_heap; using std::push_heap; using std::pop_heap; typedef long long LL; typedef unsigned long long ULL; typedef unsigned UN; typedef pair<ULL, ULL> PAIR; typedef multimap<int, int> MMAP; typedef long double LF; const int MAXN(1510); const int MAXM(40010); const int MAXE(10010); const int MAXK(6); const int HSIZE(13131); const int SIGMA_SIZE(26+11); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const ULL BASE(31); const LL LIM(1e13); const int INV(-10000); const int MOD(1000000007); const double EPS(1e-7); const LF PI(acos(-1.0)); template<typename T> inline bool checkmax(T &a, T b){if(b > a) { a = b; return true;} return false;} template<typename T> inline bool checkmin(T &a, T b){if(b < a) { a = b; return true;} return false;} template<typename T> inline T ABS(T a){return a < 0? -a: a;} template<typename T> inline bool EZ(T a){return ABS(a) < EPS;} LL dp[20][20]; int dig[20], tdig[20]; LL dfs(int len, int tl, bool bound, bool zero) { if(len+1 == tl/2) //通过前一半可以得出结果 { if(!zero && !bound) return 1; int add = (tl&1)? 2: 1; for(int i = 0; i <= len; ++i) tdig[len-i] = tdig[len+i+add]; for(int i = len; i >= 0; --i) { if(tdig[i] < dig[i]) return 1; if(tdig[i] > dig[i]) return 0; } return 1; } if(!zero && !bound && dp[len][tl] != -1) return dp[len][tl]; int up = bound? dig[len]: 9; LL ret = 0; for(int i = 0; i <= up; ++i) { tdig[len] = i; ret += dfs(len-1, tl? tl: (zero && i == 0? 0: len+1), bound && i == up, zero && i == 0); } if(!zero && !bound && len+1 > tl/2) dp[len][tl] = ret; //只对前一半dp return ret; } LL fun(LL n) { int len = 0; while(true) { dig[len] = n%10; n /= 10; if(n == 0) break; ++len; } return dfs(len, 0, true, true)-1; } int main() { int n; memset(dp, -1, sizeof(dp)); while(scanf("%d", &n), n) { LL lo = 1, hi = 1LL << 62; while(lo < hi) { LL mi = lo+(hi-lo)/2; if(fun(mi) < n) lo = mi+1; else hi = mi; } printf("%I64d\n", lo); } return 0; }
http://acm.uestc.edu.cn/problem.php?pid=1191
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <queue> #include <algorithm> #include <vector> #include <cstring> #include <stack> #include <cctype> #include <utility> #include <map> #include <string> #include <climits> #include <set> #include <string> #include <sstream> #include <utility> #include <ctime> #include <bitset> #include <iomanip> //#pragma comment(linker, "/STACK:102400000,102400000") using std::priority_queue; using std::vector; using std::swap; using std::stack; using std::sort; using std::max; using std::min; using std::pair; using std::map; using std::string; using std::cin; using std::cout; using std::set; using std::queue; using std::string; using std::stringstream; using std::make_pair; using std::getline; using std::greater; using std::endl; using std::multimap; using std::deque; using std::unique; using std::lower_bound; using std::random_shuffle; using std::bitset; using std::upper_bound; using std::multiset; using std::ios; using std::make_heap; using std::push_heap; using std::pop_heap; typedef long long LL; typedef unsigned long long ULL; typedef unsigned UN; typedef pair<ULL, ULL> PAIR; typedef multimap<int, int> MMAP; typedef long double LF; const int MAXN(1510); const int MAXM(40010); const int MAXE(10010); const int MAXK(6); const int HSIZE(13131); const int SIGMA_SIZE(26+11); const int MAXH(18); const int INFI((INT_MAX-1) >> 1); const ULL BASE(31); const LL LIM(1e13); const int INV(-10000); const int MOD(1000000007); const double EPS(1e-7); const LF PI(acos(-1.0)); template<typename T> inline bool checkmax(T &a, T b){if(b > a) { a = b; return true;} return false;} template<typename T> inline bool checkmin(T &a, T b){if(b < a) { a = b; return true;} return false;} template<typename T> inline T ABS(T a){return a < 0? -a: a;} template<typename T> inline bool EZ(T a){return ABS(a) < EPS;} LL dp[22][22]; int dig[22], tdig[22]; LL dfs(int len, int tl, bool bound, bool zero) { if(len+1 == tl/2) { if(!zero && !bound) return 1; int add = (tl&1)? 2: 1; for(int i = 0; i <= len; ++i) tdig[len-i] = tdig[len+i+add]; for(int i = len; i >= 0; --i) { if(tdig[i] < dig[i]) return 1; if(tdig[i] > dig[i]) return 0; } return 1; } if(!zero && !bound && dp[len][tl] != -1) return dp[len][tl]; int up = bound? dig[len]: 9; LL ret = 0; for(int i = 0; i <= up; ++i) { tdig[len] = i; if(zero) { if(i != 0 && ((len+1)&1) == 0) continue; ret += dfs(len-1, i == 0? 0: len+1, bound && i == up, i == 0); } else { if(i == tdig[len+1]) continue; ret += dfs(len-1, tl, bound && i == up, false); } } if(!zero && !bound && len+1 > tl/2) dp[len][tl] = ret; return ret; } LL fun(ULL n) { int len = 0; while(true) { dig[len] = n%10; n /= 10; if(n == 0) break; ++len; } return dfs(len, 0, true, true); } int main() { memset(dp, -1, sizeof(dp)); int TC; scanf("%d", &TC); while(TC--) { ULL a, b; scanf("%llu%llu", &a, &b); LL re = fun(b)-(a? fun(a-1): 0); printf("%lld\n", re); } return 0; }