1、度的数量
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 35;
int K, B;
//从a个数中选b个数的方案数
int f[N][N];
void init()
{
f[0][0]=1;
for (int i = 1; i < N; i ++ )
for (int j = 0; j <= i; j ++ )
if (!j) f[i][j] = 1;
else f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
}
int dp(int n)
{
if (!n) return 0;
//将边界 l-1,r 处理成b进制数
vector<int> nums;
while (n) nums.push_back(n % B), n /= B;
//记录满足情况的数据
int res = 0;
//假设枚举到了i-2位,i和i-1位中有几个1,由last记录
int last = 0;
for (int i = nums.size() - 1; i >= 0; i -- )
{
int x = nums[i];
//x是否>0
if (x) // 求左边分支中的数的个数
{
//大于0就将i位为0的情况记录下来
//由于i=0,i-1~0 总共由i个数据 所以从i个数据中选
res += f[i][K - last];
//当x>1时,第i位可以为1
if (x > 1)
{
//判断k是否装满
//i个数据,k-last-1 是由于i位填写需要减1
if (K - last - 1 >= 0) res += f[i][K - last - 1];
//因为题目要求是由k个互不相等的b的整数次幂之和
//数据表示成b进制只能含0,1 不能含 2~b-1
//因此x>1只包含x=1一种情况
break;
}
//x=1 需要判断右分支
else
{
last ++ ;
//last超过k就结束循环
if (last > K) break;
}
}
//特殊处理右分支的最后一个数 !i 最后一个数 last==k 是恰好装满k
//x=1,x>1的情况在以上分支被处理,x=0满足题目要求。
//因为题目要求是由k个互不相等的b的整数次幂之和
//数据表示成b进制只能含0,1 不能含 2~b-1
if (!i && last == K) res ++ ; // 最右侧分支上的方案
}
return res;
}
int main()
{
init();
int l, r;
cin >> l >> r >> K >> B;
cout << dp(r) - dp(l - 1) << endl;
return 0;
}
2、数字游戏
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//2^31 是20亿,一共10位数,写成15位正合适
const int N = 15;
int f[N][N]; // f[i, j]表示一共有i位,且最高位填j的数的个数
void init()
{
//共1位,无论首位是几,都是1方案
for (int i = 0; i <= 9; i ++ ) f[1][i] = 1;
//从2位开始枚举
for (int i = 2; i < N; i ++ )
//首位从0到9
for (int j = 0; j <= 9; j ++ )
//k是枚举第二位是几
for (int k = j; k <= 9; k ++ )
f[i][j] += f[i - 1][k];
}
int dp(int n)
{
//特判n为0,也是一种方案
if (!n) return 1;
//将数据拆开
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
//记录结果
int res = 0;
//记录前一位是几
int last = 0;
//诸位枚举
for (int i = nums.size() - 1; i >= 0; i -- )
{
int x = nums[i];
//左侧计算 枚举最高为填last~an-1的方案
for (int j = last; j < x; j ++ )
res += f[i + 1][j];
//右侧计算 判断当前一位能否为x 前一位比x大时 不行
if (x < last) break;
last = x;
//判断最右侧分支,以上代码已将左最一层左侧分支计算完毕
//并且保证每一个数都是大于等于前一个数
if (!i) res ++ ;
}
return res;
}
int main()
{
init();
int l, r;
while (cin >> l >> r) cout << dp(r) - dp(l - 1) << endl;
return 0;
}
3、Windy数
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//最多的位数 总共有10位
const int N = 11;
//一共有i位,且最高位是j
int f[N][10];
void init()
{
//f[2][2] 一共有2位,最高位是2.f[1][0]=1时,f[2][2]中的f[2][0]才能迭代
for (int i = 0; i <= 9; i ++ ) f[1][i] = 1;
for (int i = 2; i < N; i ++ )
//j是高位
for (int j = 0; j <= 9; j ++ )
//k是高位的下一位
for (int k = 0; k <= 9; k ++ )
if (abs(j - k) >= 2)
f[i][j] += f[i - 1][k];
}
int dp(int n)
{
//0不是windy,因为0不在题目范围内
if (!n) return 0;
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
int res = 0;
//为了让首位数字任意填,让首位数和0~9之间的任意数的差都>=2的数
int last = -2;
//循环是处理与最高位数字相关联的windy数
for (int i = nums.size() - 1; i >= 0; i -- )
{
int x = nums[i];
//枚举左边的分支
//j不是每一次都可以从0开始算
//当j是最高位的时候,只能从1开始
for (int j = i == nums.size() - 1; j < x; j ++ )
if (abs(j - last) >= 2)
//从i位到0位 i+1位数
res += f[i + 1][j];
if (abs(x - last) >= 2) last = x;
else break;
//右最后一个分支
if (!i) res ++ ;
}
//低于n位的数,需要特判
// 特殊处理有前导零的数
//从第一位开始枚举
//这一层特判是叠加比最高位位数低的windy数
//例如1000 有四位数 则3位和2位以及1位所有windy数均在范围内
for (int i = 1; i < nums.size(); i ++ )
//枚举最高位填多少 由于是i位数,最高位不填0
for (int j = 1; j <= 9; j ++ )
res += f[i][j];
return res;
}
int main()
{
init();
int l, r;
cin >> l >> r;
cout << dp(r) - dp(l - 1) << endl;
return 0;
}
4、数字游戏2
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//最多10位数 N表示位数 M表示取模的数字
const int N = 11, M = 110;
//取模
int P;
//f[i][j][k] 一共有i位,最高位是j,各位之和相对于M的余数是k
int f[N][10][M];
//c++取模比较特殊 负数取模是负数,整数取模是正数
//现在是想让负数和正数都取模变成正数
int mod(int x, int y)
{
return (x % y + y) % y;
}
void init()
{
//多组测试数据 需要清空
memset(f, 0, sizeof f);
for (int i = 0; i <= 9; i ++ ) f[1][i][i % P] ++ ;
//位数
for (int i = 2; i < N; i ++ )
//最高位的数据
for (int j = 0; j <= 9; j ++ )
//余数
for (int k = 0; k < P; k ++ )
//枚举次高位填什么
for (int x = 0; x <= 9; x ++ )
//f[i][j][k] 当有i位数,最高位是j,且余数是k
//f[i-1][x][mod(k-j,P)] 当有i-1位数,最高位是x,且余数是
//(k-j)含有最高位在内的所有位数之和取余的余数减去最高位 再取模得到
//包含次高位在内的所有余数之和。也就是通过这个处理建立了最高位数据和次高位数据之间的关系
f[i][j][k] += f[i - 1][x][mod(k - j, P)];
}
int dp(int n)
{
if (!n) return 1;
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
int res = 0;
//前边各位数字之和
int last = 0;
for (int i = nums.size() - 1; i >= 0; i -- )
{
//处理左边分支
int x = nums[i];
for (int j = 0; j < x; j ++ )
//通过mod(-last,P)赖筛选需要相加的数据。
res += f[i + 1][j][mod(-last, P)];
//右边分支
last += x;
//右侧分支最后一个数 ,如果是最后一个数,last已经将所有的数据累加完毕
if (!i && last % P == 0) res ++ ;
}
return res;
}
int main()
{
int l, r;
while (cin >> l >> r >> P)
{
init();
cout << dp(r) - dp(l - 1) << endl;
}
return 0;
}
5、不要62
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 10;
int f[N][10];
void init()
{
for (int i = 0; i <= 9; i ++ )
if (i != 4)
f[1][i] = 1;
for (int i = 2; i < N; i ++ )
for (int j = 0; j <= 9; j ++ )
{
if (j == 4) continue;
for (int k = 0; k <= 9; k ++ )
{
if (k == 4 || j == 6 && k == 2) continue;
f[i][j] += f[i - 1][k];
}
}
}
int dp(int n)
{
if (!n) return 1;
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
int res = 0;
int last = 0;
for (int i = nums.size() - 1; i >= 0; i -- )
{
int x = nums[i];
for (int j = 0; j < x; j ++ )
{
if (j == 4 || last == 6 && j == 2) continue;
res += f[i + 1][j];
}
if (x == 4 || last == 6 && x == 2) break;
last = x;
if (!i) res ++ ;
}
return res;
}
int main()
{
init();
int l, r;
while (cin >> l >> r, l || r)
{
cout << dp(r) - dp(l - 1) << endl;
}
return 0;
}
6、恨7不成妻
此处合法数是i-1位
此处的合法数是i位
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
//N表示位数
const int N = 20, P = 1e9 + 7;
//f[i][j][a][b] 一共由i位数,最高位是j,数本身模7是a,所有数位之和模7是b
struct F
{
//含有i位的状态方程推出i-1位的状态方程
//s0 i-1位中合法状态数据个数 s1 i-1位中合法状态数据和
//s2 i-1位中合法状态数据平方和
int s0, s1, s2;
}f[N][10][7][7];
//10的多少幂模7 10的多少次幂模p的结果
int power7[N], power9[N];
int mod(LL x, int y)
{
return (x % y + y) % y;
}
void init()
{
for (int i = 0; i <= 9; i ++ )
{
if (i == 7) continue;
auto& v = f[1][i][i % 7][i % 7];
v.s0 ++, v.s1 += i, v.s2 += i * i;
}
LL power = 10;
for (int i = 2; i < N; i ++, power *= 10)
for (int j = 0; j <= 9; j ++ )
{
if (j == 7) continue;
for (int a = 0; a < 7; a ++ )
for (int b = 0; b < 7; b ++ )
for (int k = 0; k <= 9; k ++ )
{
if (k == 7) continue;
auto &v1 = f[i][j][a][b], &v2 = f[i - 1][k][mod(a - j * power, 7)][mod(b - j, 7)];
v1.s0 = mod(v1.s0 + v2.s0, P);
v1.s1 = mod(v1.s1 + v2.s1 + j * (power % P) % P * v2.s0, P);
v1.s2 = mod(v1.s2 + j * j * (power % P) % P * (power % P) % P * v2.s0 + v2.s2 + 2 * j * power % P * v2.s1, P);
}
}
power7[0] = 1;
for (int i = 1; i < N; i ++ ) power7[i] = power7[i - 1] * 10 % 7;
power9[0] = 1;
//ll long long
for (int i = 1; i < N; i ++ ) power9[i] = power9[i - 1] * 10ll % P;
}
F get(int i, int j, int a, int b)
{
int s0 = 0, s1 = 0, s2 = 0;
for (int x = 0; x < 7; x ++ )
for (int y = 0; y < 7; y ++ )
if (x != a && y != b)
{
auto v = f[i][j][x][y];
s0 = (s0 + v.s0) % P;
s1 = (s1 + v.s1) % P;
s2 = (s2 + v.s2) % P;
}
return {s0, s1, s2};
}
int dp(LL n)
{
if (!n) return 0;
//原数
LL backup_n = n % P;
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
int res = 0;
//a前边数的本身 b前边数的各位数之和
LL last_a = 0, last_b = 0;
for (int i = nums.size() - 1; i >= 0; i -- )
{
int x = nums[i];
for (int j = 0; j < x; j ++ )
{
if (j == 7) continue;
int a = mod(-last_a * power7[i + 1], 7);
int b = mod(-last_b, 7);
//题目的性质
//一共i+1位,最高位是j,且模7后,不为a,不为b
auto v = get(i + 1, j, a, b);
res = mod(
res +
(last_a % P) * (last_a % P) % P * power9[i + 1] % P * power9[i + 1] % P * v.s0 % P +
v.s2 +
2 * last_a % P * power9[i + 1] % P * v.s1,
P);
}
if (x == 7) break;
last_a = last_a * 10 + x;
last_b += x;
//last_a%7!=0
if (!i && last_a % 7 && last_b % 7) res = (res + backup_n * backup_n) % P;
}
return res;
}
int main()
{
int T;
cin >> T;
init();
while (T -- )
{
LL l, r;
cin >> l >> r;
cout << mod(dp(r) - dp(l - 1), P) << endl;
}
return 0;
}